diff --git a/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java b/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java
index b3b9532bbec..fa68bfd5821 100644
--- a/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java
+++ b/Mage.Sets/src/mage/cards/a/AngelOfSerenity.java
@@ -55,7 +55,8 @@ public final class AngelOfSerenity extends CardImpl {
this.addAbility(ability);
// When Angel of Serenity leaves the battlefield, return the exiled cards to their owners' hands.
- this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.HAND).withText(true, true), false));
+ this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.HAND)
+ .withText(true, true, false), false));
}
private AngelOfSerenity(final AngelOfSerenity card) {
diff --git a/Mage.Sets/src/mage/cards/b/BomatCourier.java b/Mage.Sets/src/mage/cards/b/BomatCourier.java
index 4ae9d51a81e..ccf0f49a78b 100644
--- a/Mage.Sets/src/mage/cards/b/BomatCourier.java
+++ b/Mage.Sets/src/mage/cards/b/BomatCourier.java
@@ -3,23 +3,18 @@ package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
-import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.DiscardHandCost;
import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.costs.mana.ColoredManaCost;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ExileCardsFromTopOfLibraryControllerEffect;
+import mage.abilities.effects.common.ReturnFromExileForSourceEffect;
import mage.abilities.keyword.HasteAbility;
-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;
/**
*
@@ -37,10 +32,15 @@ public final class BomatCourier extends CardImpl {
this.addAbility(HasteAbility.getInstance());
// Whenever Bomat Courier attacks, exile the top card of your library face down.
- this.addAbility(new AttacksTriggeredAbility(new BomatCourierExileEffect(), false));
+ this.addAbility(new AttacksTriggeredAbility(
+ new ExileCardsFromTopOfLibraryControllerEffect(1, true, true, true),
+ false));
// {R}, Discard your hand, Sacrifice Bomat Courier: Put all cards exiled with Bomat Courier into their owners' hands.
- Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BomatCourierReturnEffect(), new ColoredManaCost(ColoredManaSymbol.R));
+ Ability ability = new SimpleActivatedAbility(
+ Zone.BATTLEFIELD,
+ new ReturnFromExileForSourceEffect(Zone.HAND).withText(true, true, true),
+ new ColoredManaCost(ColoredManaSymbol.R));
ability.addCost(new DiscardHandCost());
ability.addCost(new SacrificeSourceCost());
this.addAbility(ability);
@@ -55,67 +55,3 @@ public final class BomatCourier extends CardImpl {
return new BomatCourier(this);
}
}
-
-class BomatCourierExileEffect extends OneShotEffect {
-
- BomatCourierExileEffect() {
- super(Outcome.Exile);
- this.staticText = "exile the top card of your library face down";
- }
-
- private BomatCourierExileEffect(final BomatCourierExileEffect effect) {
- super(effect);
- }
-
- @Override
- public BomatCourierExileEffect copy() {
- return new BomatCourierExileEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- Player controller = game.getPlayer(source.getControllerId());
- MageObject sourceObject = source.getSourceObject(game);
- if (controller != null && sourceObject != null) {
- Card card = controller.getLibrary().getFromTop(game);
- if (card != null) {
- UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
- card.setFaceDown(true, game);
- controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName());
- card.setFaceDown(true, game);
- return true;
- }
- }
- return false;
- }
-}
-
-class BomatCourierReturnEffect extends OneShotEffect {
-
- BomatCourierReturnEffect() {
- super(Outcome.DrawCard);
- this.staticText = "Put all cards exiled with {this} into their owners' hands";
- }
-
- private BomatCourierReturnEffect(final BomatCourierReturnEffect effect) {
- super(effect);
- }
-
- @Override
- public BomatCourierReturnEffect copy() {
- return new BomatCourierReturnEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- Player controller = game.getPlayer(source.getControllerId());
- if (controller != null) {
- ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()));
- if (exileZone != null) {
- controller.moveCards(exileZone, Zone.HAND, source, game);
- }
- return true;
- }
- return false;
- }
-}
diff --git a/Mage.Sets/src/mage/cards/c/ConnectingTheDots.java b/Mage.Sets/src/mage/cards/c/ConnectingTheDots.java
new file mode 100644
index 00000000000..0e634309abd
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/c/ConnectingTheDots.java
@@ -0,0 +1,48 @@
+package mage.cards.c;
+
+import java.util.UUID;
+
+import mage.abilities.Ability;
+import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.DiscardHandCost;
+import mage.abilities.costs.common.SacrificeSourceCost;
+import mage.abilities.costs.mana.ManaCostsImpl;
+import mage.abilities.effects.common.ExileCardsFromTopOfLibraryControllerEffect;
+import mage.abilities.effects.common.ReturnFromExileForSourceEffect;
+import mage.cards.CardImpl;
+import mage.cards.CardSetInfo;
+import mage.constants.CardType;
+import mage.constants.Zone;
+
+/**
+ * @author Cguy7777
+ */
+public final class ConnectingTheDots extends CardImpl {
+
+ public ConnectingTheDots(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}");
+
+ // Whenever a creature you control attacks, exile the top card of your library face down.
+ this.addAbility(new AttacksCreatureYouControlTriggeredAbility(
+ new ExileCardsFromTopOfLibraryControllerEffect(1, true, true, true)));
+
+ // {1}{R}, Discard your hand, Sacrifice Connecting the Dots: Put all cards exiled with Connecting the Dots into their owners' hands.
+ Ability ability = new SimpleActivatedAbility(
+ new ReturnFromExileForSourceEffect(Zone.HAND)
+ .withText(true, true, true),
+ new ManaCostsImpl<>("{1}{R}"));
+ ability.addCost(new DiscardHandCost());
+ ability.addCost(new SacrificeSourceCost());
+ this.addAbility(ability);
+ }
+
+ private ConnectingTheDots(final ConnectingTheDots card) {
+ super(card);
+ }
+
+ @Override
+ public ConnectingTheDots copy() {
+ return new ConnectingTheDots(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/e/ExpertLevelSafe.java b/Mage.Sets/src/mage/cards/e/ExpertLevelSafe.java
new file mode 100644
index 00000000000..629216547b3
--- /dev/null
+++ b/Mage.Sets/src/mage/cards/e/ExpertLevelSafe.java
@@ -0,0 +1,95 @@
+package mage.cards.e;
+
+import java.util.UUID;
+
+import mage.abilities.Ability;
+import mage.abilities.common.EntersBattlefieldTriggeredAbility;
+import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.TapSourceCost;
+import mage.abilities.costs.mana.GenericManaCost;
+import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ExileCardsFromTopOfLibraryControllerEffect;
+import mage.abilities.effects.common.ReturnFromExileForSourceEffect;
+import mage.abilities.effects.common.SacrificeSourceEffect;
+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;
+
+/**
+ * @author Cguy7777
+ */
+public final class ExpertLevelSafe extends CardImpl {
+
+ public ExpertLevelSafe(UUID ownerId, CardSetInfo setInfo) {
+ super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
+
+ // When Expert-Level Safe enters the battlefield, exile the top two cards of your library face down.
+ this.addAbility(new EntersBattlefieldTriggeredAbility(
+ new ExileCardsFromTopOfLibraryControllerEffect(2, true, true)));
+
+ // {1}, {T}: You and target opponent each secretly choose 1, 2, or 3. Then those choices are revealed.
+ // If they match, sacrifice Expert-Level Safe and put all cards exiled with it into their owners' hands.
+ // Otherwise, exile the top card of your library face down.
+ Ability ability = new SimpleActivatedAbility(new ExpertLevelSafeEffect(), new GenericManaCost(1));
+ ability.addCost(new TapSourceCost());
+ ability.addTarget(new TargetOpponent());
+ this.addAbility(ability);
+ }
+
+ private ExpertLevelSafe(final ExpertLevelSafe card) {
+ super(card);
+ }
+
+ @Override
+ public ExpertLevelSafe copy() {
+ return new ExpertLevelSafe(this);
+ }
+}
+
+class ExpertLevelSafeEffect extends OneShotEffect {
+
+ ExpertLevelSafeEffect() {
+ super(Outcome.Benefit);
+ staticText = "you and target opponent each secretly choose 1, 2, or 3. Then those choices are revealed. " +
+ "If they match, sacrifice {this} and put all cards exiled with it into their owners' hands. " +
+ "Otherwise, exile the top card of your library face down";
+ }
+
+ protected ExpertLevelSafeEffect(ExpertLevelSafeEffect effect) {
+ super(effect);
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ Player controller = game.getPlayer(source.getControllerId());
+ Player opponent = game.getPlayer(source.getFirstTarget());
+ if (controller == null || opponent == null) {
+ return false;
+ }
+
+ int controllerChoice = controller.getAmount(1, 3, "Choose a number", game);
+ int opponentChoice = opponent.getAmount(1, 3, "Choose a number", game);
+
+ game.informPlayers(controller.getLogName() + " chose " + controllerChoice);
+ game.informPlayers(opponent.getLogName() + " chose " + opponentChoice);
+
+ if (controllerChoice == opponentChoice) {
+ new SacrificeSourceEffect().apply(game, source);
+ new ReturnFromExileForSourceEffect(Zone.HAND).apply(game, source);
+ return true;
+ }
+
+ new ExileCardsFromTopOfLibraryControllerEffect(1, true, true).apply(game, source);
+ return true;
+ }
+
+ @Override
+ public ExpertLevelSafeEffect copy() {
+ return new ExpertLevelSafeEffect(this);
+ }
+}
diff --git a/Mage.Sets/src/mage/cards/f/FreeForAll.java b/Mage.Sets/src/mage/cards/f/FreeForAll.java
index 630f6bc995c..6a858935ef0 100644
--- a/Mage.Sets/src/mage/cards/f/FreeForAll.java
+++ b/Mage.Sets/src/mage/cards/f/FreeForAll.java
@@ -5,6 +5,7 @@ import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ReturnFromExileForSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
@@ -36,7 +37,8 @@ public final class FreeForAll extends CardImpl {
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new FreeForAllReturnFromExileEffect(), TargetController.ANY, false));
// When Free-for-All leaves the battlefield, put all cards exiled with it into their owners' graveyards.
- this.addAbility(new LeavesBattlefieldTriggeredAbility(new FreeForAllLeavesBattlefieldEffect(), false));
+ this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.GRAVEYARD)
+ .setText("put all cards exiled with it into their owners' graveyards"), false));
}
private FreeForAll(final FreeForAll card) {
@@ -111,29 +113,3 @@ class FreeForAllReturnFromExileEffect extends OneShotEffect {
return player.moveCards(exiledCards.getRandom(game), Zone.BATTLEFIELD, source, game);
}
}
-
-class FreeForAllLeavesBattlefieldEffect extends OneShotEffect {
-
- FreeForAllLeavesBattlefieldEffect() {
- super(Outcome.Detriment);
- this.staticText = "put all cards exiled with it into their owners' graveyards";
- }
-
- private FreeForAllLeavesBattlefieldEffect(final FreeForAllLeavesBattlefieldEffect effect) {
- super(effect);
- }
-
- @Override
- public FreeForAllLeavesBattlefieldEffect copy() {
- return new FreeForAllLeavesBattlefieldEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- Player controller = game.getPlayer(source.getControllerId());
- ExileZone exZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source));
- return controller != null
- && exZone != null
- && controller.moveCards(exZone.getCards(game), Zone.GRAVEYARD, source, game);
- }
-}
diff --git a/Mage.Sets/src/mage/cards/g/Gravegouger.java b/Mage.Sets/src/mage/cards/g/Gravegouger.java
index e16cf4f74bd..78d25387bb8 100644
--- a/Mage.Sets/src/mage/cards/g/Gravegouger.java
+++ b/Mage.Sets/src/mage/cards/g/Gravegouger.java
@@ -34,7 +34,8 @@ public final class Gravegouger extends CardImpl {
this.addAbility(ability);
// When Gravegouger leaves the battlefield, return the exiled cards to their owner's graveyard.
- this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.GRAVEYARD).withText(true, false), false));
+ this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.GRAVEYARD)
+ .withText(true, false, false), false));
}
private Gravegouger(final Gravegouger card) {
diff --git a/Mage.Sets/src/mage/cards/k/KnowledgeVault.java b/Mage.Sets/src/mage/cards/k/KnowledgeVault.java
index c5bf3c62143..95665972de9 100644
--- a/Mage.Sets/src/mage/cards/k/KnowledgeVault.java
+++ b/Mage.Sets/src/mage/cards/k/KnowledgeVault.java
@@ -2,25 +2,23 @@
package mage.cards.k;
import java.util.UUID;
-import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
+import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ExileCardsFromTopOfLibraryControllerEffect;
import mage.abilities.effects.common.ReturnFromExileForSourceEffect;
import mage.abilities.effects.common.discard.DiscardHandControllerEffect;
-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.ExileZone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
-import mage.util.CardUtil;
/**
*
@@ -32,13 +30,20 @@ public final class KnowledgeVault extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
// {2}, {T}: Exile the top card of your library face down.
- this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new KnowledgeVaultExileEffect(), new GenericManaCost(2)));
+ Ability ability = new SimpleActivatedAbility(
+ Zone.BATTLEFIELD,
+ new ExileCardsFromTopOfLibraryControllerEffect(1, true, true),
+ new GenericManaCost(2));
+ ability.addCost(new TapSourceCost());
+ this.addAbility(ability);
// {0}: Sacrifice Knowledge Vault. If you do, discard your hand, then put all cards exiled with Knowledge Vault into their owner’s hand.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new KnowledgeVaultReturnEffect(), new GenericManaCost(0)));
// When Knowledge Vault leaves the battlefield, put all cards exiled with Knowledge Vault into their owner’s graveyard.
- this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.GRAVEYARD), false));
+ this.addAbility(new LeavesBattlefieldTriggeredAbility(
+ new ReturnFromExileForSourceEffect(Zone.GRAVEYARD).withText(true, false, true),
+ false));
}
private KnowledgeVault(final KnowledgeVault card) {
@@ -51,40 +56,6 @@ public final class KnowledgeVault extends CardImpl {
}
}
-class KnowledgeVaultExileEffect extends OneShotEffect {
-
- KnowledgeVaultExileEffect() {
- super(Outcome.Exile);
- this.staticText = "exile the top card of your library face down";
- }
-
- private KnowledgeVaultExileEffect(final KnowledgeVaultExileEffect effect) {
- super(effect);
- }
-
- @Override
- public KnowledgeVaultExileEffect copy() {
- return new KnowledgeVaultExileEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- Player controller = game.getPlayer(source.getControllerId());
- MageObject sourceObject = source.getSourceObject(game);
- if (controller != null && sourceObject != null) {
- Card card = controller.getLibrary().getFromTop(game);
- if (card != null) {
- UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
- card.setFaceDown(true, game);
- controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName());
- card.setFaceDown(true, game);
- return true;
- }
- }
- return false;
- }
-}
-
class KnowledgeVaultReturnEffect extends OneShotEffect {
KnowledgeVaultReturnEffect() {
@@ -108,10 +79,7 @@ class KnowledgeVaultReturnEffect extends OneShotEffect {
if (sourcePermanent != null && controller != null) {
if (sourcePermanent.sacrifice(source, game)) {
new DiscardHandControllerEffect().apply(game, source);
- ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()));
- if (exileZone != null) {
- controller.moveCards(exileZone, Zone.HAND, source, game);
- }
+ new ReturnFromExileForSourceEffect(Zone.HAND).apply(game, source);
}
return true;
}
diff --git a/Mage.Sets/src/mage/cards/k/KyrenArchive.java b/Mage.Sets/src/mage/cards/k/KyrenArchive.java
index 808a1a43ad2..e51f3646873 100644
--- a/Mage.Sets/src/mage/cards/k/KyrenArchive.java
+++ b/Mage.Sets/src/mage/cards/k/KyrenArchive.java
@@ -2,25 +2,19 @@
package mage.cards.k;
import java.util.UUID;
-import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.DiscardHandCost;
import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.costs.mana.GenericManaCost;
-import mage.abilities.effects.OneShotEffect;
-import mage.cards.Card;
+import mage.abilities.effects.common.ExileCardsFromTopOfLibraryControllerEffect;
+import mage.abilities.effects.common.ReturnFromExileForSourceEffect;
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.game.ExileZone;
-import mage.game.Game;
-import mage.players.Player;
-import mage.util.CardUtil;
/**
*
@@ -32,12 +26,16 @@ public final class KyrenArchive extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// At the beginning of your upkeep, you may exile the top card of your library face down.
- this.addAbility(new BeginningOfUpkeepTriggeredAbility(new KyrenArchiveExileEffect(), TargetController.YOU, true));
+ this.addAbility(new BeginningOfUpkeepTriggeredAbility(
+ new ExileCardsFromTopOfLibraryControllerEffect(1, true, true),
+ TargetController.YOU,
+ true)
+ );
// {5}, Discard your hand, Sacrifice Kyren Archive: Put all cards exiled with Kyren Archive into their owner's hand.
Ability ability = new SimpleActivatedAbility(
Zone.BATTLEFIELD,
- new KyrenArchiveReturnEffect(),
+ new ReturnFromExileForSourceEffect(Zone.HAND).withText(true, false, true),
new GenericManaCost(5)
);
ability.addCost(new DiscardHandCost());
@@ -54,67 +52,3 @@ public final class KyrenArchive extends CardImpl {
return new KyrenArchive(this);
}
}
-
-class KyrenArchiveExileEffect extends OneShotEffect {
-
- KyrenArchiveExileEffect() {
- super(Outcome.Exile);
- this.staticText = "exile the top card of your library face down";
- }
-
- private KyrenArchiveExileEffect(final KyrenArchiveExileEffect effect) {
- super(effect);
- }
-
- @Override
- public KyrenArchiveExileEffect copy() {
- return new KyrenArchiveExileEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- Player controller = game.getPlayer(source.getControllerId());
- MageObject sourceObject = source.getSourceObject(game);
- if (controller != null && sourceObject != null) {
- Card card = controller.getLibrary().getFromTop(game);
- if (card != null) {
- UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
- card.setFaceDown(true, game);
- controller.moveCardsToExile(card, source, game, false, exileZoneId, sourceObject.getIdName());
- card.setFaceDown(true, game);
- return true;
- }
- }
- return false;
- }
-}
-
-class KyrenArchiveReturnEffect extends OneShotEffect {
-
- KyrenArchiveReturnEffect() {
- super(Outcome.DrawCard);
- this.staticText = "Put all cards exiled with {this} into their owners' hands";
- }
-
- private KyrenArchiveReturnEffect(final KyrenArchiveReturnEffect effect) {
- super(effect);
- }
-
- @Override
- public KyrenArchiveReturnEffect copy() {
- return new KyrenArchiveReturnEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- Player controller = game.getPlayer(source.getControllerId());
- if (controller != null) {
- ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()));
- if (exileZone != null) {
- controller.moveCards(exileZone, Zone.HAND, source, game);
- }
- return true;
- }
- return false;
- }
-}
diff --git a/Mage.Sets/src/mage/cards/m/MysticForge.java b/Mage.Sets/src/mage/cards/m/MysticForge.java
index 09ad165fa8c..158399d614a 100644
--- a/Mage.Sets/src/mage/cards/m/MysticForge.java
+++ b/Mage.Sets/src/mage/cards/m/MysticForge.java
@@ -5,21 +5,17 @@ import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.common.TapSourceCost;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ExileCardsFromTopOfLibraryControllerEffect;
import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect;
import mage.abilities.effects.common.continuous.PlayTheTopCardEffect;
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.FilterCard;
import mage.filter.common.FilterNonlandCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorlessPredicate;
-import mage.game.Game;
-import mage.players.Player;
import java.util.UUID;
@@ -47,7 +43,7 @@ public final class MysticForge extends CardImpl {
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
// {T}, Pay 1 life: Exile the top card of your library.
- Ability ability = new SimpleActivatedAbility(new MysticForgeExileEffect(), new TapSourceCost());
+ Ability ability = new SimpleActivatedAbility(new ExileCardsFromTopOfLibraryControllerEffect(1), new TapSourceCost());
ability.addCost(new PayLifeCost(1));
this.addAbility(ability);
}
@@ -61,29 +57,3 @@ public final class MysticForge extends CardImpl {
return new MysticForge(this);
}
}
-
-class MysticForgeExileEffect extends OneShotEffect {
-
- MysticForgeExileEffect() {
- super(Outcome.Benefit);
- staticText = "exile the top card of your library";
- }
-
- private MysticForgeExileEffect(final MysticForgeExileEffect effect) {
- super(effect);
- }
-
- @Override
- public MysticForgeExileEffect copy() {
- return new MysticForgeExileEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- Player controller = game.getPlayer(source.getControllerId());
- if (controller == null) {
- return false;
- }
- return controller.moveCards(controller.getLibrary().getFromTop(game), Zone.EXILED, source, game);
- }
-}
diff --git a/Mage.Sets/src/mage/cards/p/Petradon.java b/Mage.Sets/src/mage/cards/p/Petradon.java
index 77b0fe40694..634ae5333d8 100644
--- a/Mage.Sets/src/mage/cards/p/Petradon.java
+++ b/Mage.Sets/src/mage/cards/p/Petradon.java
@@ -15,7 +15,6 @@ import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.Zone;
-import mage.filter.StaticFilters;
import mage.target.common.TargetLandPermanent;
import java.util.UUID;
@@ -38,7 +37,8 @@ public final class Petradon extends CardImpl {
this.addAbility(ability);
// When Petradon leaves the battlefield, return the exiled cards to the battlefield under their owners' control.
- this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD).withText(true, true), false));
+ this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD)
+ .withText(true, true, false), false));
// {R}: Petradon gets +1/+0 until end of turn.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(1, 0, Duration.EndOfTurn), new ManaCostsImpl<>("{R}")));
diff --git a/Mage.Sets/src/mage/cards/p/PrecognitionField.java b/Mage.Sets/src/mage/cards/p/PrecognitionField.java
index fda488a38f6..1641ba976c8 100644
--- a/Mage.Sets/src/mage/cards/p/PrecognitionField.java
+++ b/Mage.Sets/src/mage/cards/p/PrecognitionField.java
@@ -1,23 +1,17 @@
package mage.cards.p;
-import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.GenericManaCost;
-import mage.abilities.effects.OneShotEffect;
+import mage.abilities.effects.common.ExileCardsFromTopOfLibraryControllerEffect;
import mage.abilities.effects.common.continuous.LookAtTopCardOfLibraryAnyTimeEffect;
import mage.abilities.effects.common.continuous.PlayTheTopCardEffect;
-import mage.cards.Card;
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.FilterCard;
import mage.filter.predicate.Predicates;
-import mage.game.Game;
-import mage.players.Player;
import java.util.UUID;
@@ -45,7 +39,7 @@ public final class PrecognitionField extends CardImpl {
this.addAbility(new SimpleStaticAbility(new PlayTheTopCardEffect(TargetController.YOU, filter, false)));
// {3}: Exile the top card of your library.
- this.addAbility(new SimpleActivatedAbility(new PrecognitionFieldExileEffect(), new GenericManaCost(3)));
+ this.addAbility(new SimpleActivatedAbility(new ExileCardsFromTopOfLibraryControllerEffect(1), new GenericManaCost(3)));
}
private PrecognitionField(final PrecognitionField card) {
@@ -57,33 +51,3 @@ public final class PrecognitionField extends CardImpl {
return new PrecognitionField(this);
}
}
-
-class PrecognitionFieldExileEffect extends OneShotEffect {
-
- PrecognitionFieldExileEffect() {
- super(Outcome.Benefit);
- staticText = "exile the top card of your library";
- }
-
- private PrecognitionFieldExileEffect(final PrecognitionFieldExileEffect effect) {
- super(effect);
- }
-
- @Override
- public PrecognitionFieldExileEffect copy() {
- return new PrecognitionFieldExileEffect(this);
- }
-
- @Override
- 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.moveCards(card, Zone.EXILED, source, game);
- }
- return true;
- }
- return false;
- }
-}
diff --git a/Mage.Sets/src/mage/cards/r/RonaDiscipleOfGix.java b/Mage.Sets/src/mage/cards/r/RonaDiscipleOfGix.java
index a206032303c..662ef87239a 100644
--- a/Mage.Sets/src/mage/cards/r/RonaDiscipleOfGix.java
+++ b/Mage.Sets/src/mage/cards/r/RonaDiscipleOfGix.java
@@ -11,7 +11,7 @@ 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.OneShotEffect;
+import mage.abilities.effects.common.ExileCardsFromTopOfLibraryControllerEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
@@ -26,7 +26,6 @@ import mage.constants.Zone;
import mage.filter.common.FilterHistoricCard;
import mage.game.ExileZone;
import mage.game.Game;
-import mage.players.Player;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
@@ -59,7 +58,10 @@ public final class RonaDiscipleOfGix extends CardImpl {
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new RonaDiscipleOfGixPlayNonLandEffect()));
// {4}, {T}: Exile the top card of your library.
- ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RonaDiscipleOfGixExileEffect(), new GenericManaCost(4));
+ ability = new SimpleActivatedAbility(
+ Zone.BATTLEFIELD,
+ new ExileCardsFromTopOfLibraryControllerEffect(1, true),
+ new GenericManaCost(4));
ability.addCost(new TapSourceCost());
this.addAbility(ability);
}
@@ -111,35 +113,3 @@ class RonaDiscipleOfGixPlayNonLandEffect extends AsThoughEffectImpl {
return false;
}
}
-
-class RonaDiscipleOfGixExileEffect extends OneShotEffect {
-
- RonaDiscipleOfGixExileEffect() {
- super(Outcome.Exile);
- this.staticText = "Exile the top card of your library";
- }
-
- private RonaDiscipleOfGixExileEffect(final RonaDiscipleOfGixExileEffect effect) {
- super(effect);
- }
-
- @Override
- public RonaDiscipleOfGixExileEffect copy() {
- return new RonaDiscipleOfGixExileEffect(this);
- }
-
- @Override
- public boolean apply(Game game, Ability source) {
- Player controller = game.getPlayer(source.getControllerId());
- MageObject sourceObject = source.getSourceObject(game);
- if (controller != null && sourceObject != null) {
- Card card = controller.getLibrary().getFromTop(game);
- if (card != null) {
- UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
- controller.moveCardsToExile(card, source, game, true, exileId, sourceObject.getIdName());
- }
- return true;
- }
- return false;
- }
-}
diff --git a/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java b/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java
index 8cd90df81c2..d17b81bd724 100644
--- a/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java
+++ b/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java
@@ -6,8 +6,8 @@ import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.AsThoughEffectImpl;
-import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageTargetEffect;
+import mage.abilities.effects.common.ExileCardsFromTopOfLibraryControllerEffect;
import mage.abilities.hint.common.OpponentsLostLifeHint;
import mage.cards.Card;
import mage.cards.CardImpl;
@@ -15,7 +15,6 @@ import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.ExileZone;
import mage.game.Game;
-import mage.players.Player;
import mage.target.common.TargetOpponentOrPlaneswalker;
import mage.util.CardUtil;
import mage.watchers.common.PlayerLostLifeWatcher;
@@ -32,7 +31,7 @@ public final class TheaterOfHorrors extends CardImpl {
// At the beginning of your upkeep, exile the top card of your library.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(
- new TheaterOfHorrorsExileEffect(),
+ new ExileCardsFromTopOfLibraryControllerEffect(1, true),
TargetController.YOU, false
));
@@ -58,39 +57,6 @@ public final class TheaterOfHorrors extends CardImpl {
}
}
-class TheaterOfHorrorsExileEffect extends OneShotEffect {
-
- TheaterOfHorrorsExileEffect() {
- super(Outcome.Benefit);
- staticText = "exile the top card of your library.";
- }
-
- private TheaterOfHorrorsExileEffect(final TheaterOfHorrorsExileEffect effect) {
- super(effect);
- }
-
- @Override
- public TheaterOfHorrorsExileEffect copy() {
- return new TheaterOfHorrorsExileEffect(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;
- }
- return player.moveCardsToExile(
- card, source, game, true, CardUtil.getCardExileZoneId(game, source),
- CardUtil.createObjectRealtedWindowTitle(source, game, null)
- );
- }
-}
-
class TheaterOfHorrorsCastEffect extends AsThoughEffectImpl {
TheaterOfHorrorsCastEffect() {
diff --git a/Mage.Sets/src/mage/cards/w/WormfangBehemoth.java b/Mage.Sets/src/mage/cards/w/WormfangBehemoth.java
index 4511da5417c..310c255b6cf 100644
--- a/Mage.Sets/src/mage/cards/w/WormfangBehemoth.java
+++ b/Mage.Sets/src/mage/cards/w/WormfangBehemoth.java
@@ -37,7 +37,7 @@ public final class WormfangBehemoth extends CardImpl {
// When Wormfang Behemoth leaves the battlefield, return the exiled cards to their owner's hand.
this.addAbility(new LeavesBattlefieldTriggeredAbility(
- new ReturnFromExileForSourceEffect(Zone.HAND).withText(true, false), false
+ new ReturnFromExileForSourceEffect(Zone.HAND).withText(true, false, false), false
));
}
diff --git a/Mage.Sets/src/mage/sets/Fallout.java b/Mage.Sets/src/mage/sets/Fallout.java
index eb211f9293e..ac6726a0139 100644
--- a/Mage.Sets/src/mage/sets/Fallout.java
+++ b/Mage.Sets/src/mage/sets/Fallout.java
@@ -70,6 +70,8 @@ public final class Fallout extends ExpansionSet {
cards.add(new SetCardInfo("Everflowing Chalice", 230, Rarity.UNCOMMON, mage.cards.e.EverflowingChalice.class));
cards.add(new SetCardInfo("Evolving Wilds", 263, Rarity.COMMON, mage.cards.e.EvolvingWilds.class));
cards.add(new SetCardInfo("Exotic Orchard", 264, Rarity.RARE, mage.cards.e.ExoticOrchard.class));
+ cards.add(new SetCardInfo("Expert-Level Safe", 133, Rarity.UNCOMMON, mage.cards.e.ExpertLevelSafe.class, NON_FULL_USE_VARIOUS));
+ cards.add(new SetCardInfo("Expert-Level Safe", 661, Rarity.UNCOMMON, mage.cards.e.ExpertLevelSafe.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Explorer's Scope", 231, Rarity.COMMON, mage.cards.e.ExplorersScope.class));
cards.add(new SetCardInfo("Farewell", 353, Rarity.RARE, mage.cards.f.Farewell.class));
cards.add(new SetCardInfo("Farseek", 197, Rarity.COMMON, mage.cards.f.Farseek.class));
diff --git a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java
index 9cade3be47c..04ad3635429 100644
--- a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java
+++ b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java
@@ -70,6 +70,8 @@ public final class MurdersAtKarlovManor extends ExpansionSet {
cards.add(new SetCardInfo("Cold Case Cracker", 46, Rarity.COMMON, mage.cards.c.ColdCaseCracker.class));
cards.add(new SetCardInfo("Commercial District", 259, Rarity.RARE, mage.cards.c.CommercialDistrict.class));
cards.add(new SetCardInfo("Concealed Weapon", 117, Rarity.UNCOMMON, mage.cards.c.ConcealedWeapon.class));
+ cards.add(new SetCardInfo("Connecting the Dots", 118, Rarity.RARE, mage.cards.c.ConnectingTheDots.class, NON_FULL_USE_VARIOUS));
+ cards.add(new SetCardInfo("Connecting the Dots", 403, Rarity.RARE, mage.cards.c.ConnectingTheDots.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Convenient Target", 119, Rarity.UNCOMMON, mage.cards.c.ConvenientTarget.class));
cards.add(new SetCardInfo("Cornered Crook", 120, Rarity.UNCOMMON, mage.cards.c.CorneredCrook.class));
cards.add(new SetCardInfo("Crime Novelist", 121, Rarity.UNCOMMON, mage.cards.c.CrimeNovelist.class));
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ExileCardsFromTopOfLibraryControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ExileCardsFromTopOfLibraryControllerEffect.java
new file mode 100644
index 00000000000..62567f40912
--- /dev/null
+++ b/Mage/src/main/java/mage/abilities/effects/common/ExileCardsFromTopOfLibraryControllerEffect.java
@@ -0,0 +1,100 @@
+package mage.abilities.effects.common;
+
+import mage.MageObject;
+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 mage.util.CardUtil;
+
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * @author Cguy7777
+ */
+public class ExileCardsFromTopOfLibraryControllerEffect extends OneShotEffect {
+
+ private final int amount;
+ private final boolean toUniqueExileZone;
+ private final boolean faceDown;
+
+ public ExileCardsFromTopOfLibraryControllerEffect(int amount) {
+ this(amount, false);
+ }
+
+ public ExileCardsFromTopOfLibraryControllerEffect(int amount, boolean toUniqueExileZone) {
+ this(amount, toUniqueExileZone, false);
+ }
+
+ public ExileCardsFromTopOfLibraryControllerEffect(int amount, boolean toUniqueExileZone, boolean faceDown) {
+ this(amount, toUniqueExileZone, faceDown, false);
+ }
+
+ /**
+ * @param amount number of cards to exile
+ * @param toUniqueExileZone moves the card to a source object dependant
+ * unique exile zone, so another effect of the same source object (e.g.
+ * Theater of Horrors) can identify the card
+ * @param faceDown if true, cards are exiled face down
+ * @param withFaceDownReminderText if true, add the reminder text for exiling one face down card
+ */
+ public ExileCardsFromTopOfLibraryControllerEffect(int amount, boolean toUniqueExileZone, boolean faceDown, boolean withFaceDownReminderText) {
+ super(Outcome.Exile);
+ this.amount = amount;
+ this.toUniqueExileZone = toUniqueExileZone;
+ this.faceDown = faceDown;
+
+ staticText = "exile the top "
+ + ((amount > 1) ? CardUtil.numberToText(amount) + " cards" : "card")
+ + " of your library"
+ + (faceDown ? " face down" : "")
+ + (withFaceDownReminderText ? ". (You can't look at it.)" : "");
+ }
+
+ protected ExileCardsFromTopOfLibraryControllerEffect(final ExileCardsFromTopOfLibraryControllerEffect effect) {
+ super(effect);
+ this.amount = effect.amount;
+ this.toUniqueExileZone = effect.toUniqueExileZone;
+ this.faceDown = effect.faceDown;
+ }
+
+ @Override
+ public boolean apply(Game game, Ability source) {
+ Player controller = game.getPlayer(source.getControllerId());
+ if (controller == null) {
+ return false;
+ }
+
+ UUID exileZoneId = null;
+ String exileZoneName = "";
+ if (toUniqueExileZone) {
+ MageObject sourceObject = source.getSourceObject(game);
+ if (sourceObject == null) {
+ return false;
+ }
+ exileZoneId = CardUtil.getExileZoneId(game, source);
+ exileZoneName = CardUtil.createObjectRealtedWindowTitle(source, game, null);
+ }
+
+ Set cards = controller.getLibrary().getTopCards(game, amount);
+ if (cards.isEmpty()) {
+ return true;
+ }
+
+ boolean exiledSuccessfully = false;
+ for (Card card : cards) {
+ card.setFaceDown(faceDown, game);
+ exiledSuccessfully |= controller.moveCardsToExile(card, source, game, !faceDown, exileZoneId, exileZoneName);
+ card.setFaceDown(faceDown, game);
+ }
+ return exiledSuccessfully;
+ }
+
+ @Override
+ public ExileCardsFromTopOfLibraryControllerEffect copy() {
+ return new ExileCardsFromTopOfLibraryControllerEffect(this);
+ }
+}
diff --git a/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java b/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java
index c0771ccab9e..0bcb084d1a0 100644
--- a/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java
+++ b/Mage/src/main/java/mage/abilities/effects/common/ReturnFromExileForSourceEffect.java
@@ -22,6 +22,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
private final Zone returnToZone;
private boolean pluralCards;
private boolean pluralOwners;
+ private boolean putPhrasing;
/**
* @param zone Zone the card should return to
@@ -31,6 +32,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
this.returnToZone = zone;
this.pluralCards = false;
this.pluralOwners = false;
+ this.putPhrasing = false;
updateText();
}
@@ -39,6 +41,7 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
this.returnToZone = effect.returnToZone;
this.pluralCards = effect.pluralCards;
this.pluralOwners = effect.pluralOwners;
+ this.putPhrasing = effect.putPhrasing;
}
@Override
@@ -77,16 +80,22 @@ public class ReturnFromExileForSourceEffect extends OneShotEffect {
return true;
}
- public ReturnFromExileForSourceEffect withText(boolean pluralCards, boolean pluralOwners) {
+ public ReturnFromExileForSourceEffect withText(boolean pluralCards, boolean pluralOwners, boolean putPhrasing) {
this.pluralCards = pluralCards;
this.pluralOwners = pluralOwners;
+ this.putPhrasing = putPhrasing;
updateText();
return this;
}
private void updateText() {
StringBuilder sb = new StringBuilder();
- sb.append("return the exiled ").append(pluralCards ? "cards" : "card").append(" to ");
+ if (putPhrasing) {
+ sb.append("put ").append(pluralCards ? "all cards " : "the card ").append("exiled with {this} ");
+ sb.append(returnToZone == Zone.BATTLEFIELD ? "onto " : "into ");
+ } else {
+ sb.append("return the exiled ").append(pluralCards ? "cards" : "card").append(" to ");
+ }
if (returnToZone == Zone.BATTLEFIELD) {
sb.append("the battlefield under ");
}