Implement [MKM] Connecting the Dots; [PIP] Expert-Level Safe; related refactoring (#11922)

* Switch to common class for exiling top cards of your library

* Replace some custom classes with ReturnFromExileForSourceEffect; text-gen additions to that Effect

* Knowledge Vault: add tap cost to 1st ability, fix text on 3rd ability; Petradon: remove unused import

* [MKM] Implement Connecting the Dots

* [PIP] Implement Expert-Level Safe

* Set staticText in constructor

* Use {this} in Expert-Level Safe text

* Use createObjectRealtedWindowTitle for exile names

* Remove unnecessary field
This commit is contained in:
Cameron Merkel 2024-03-10 23:16:42 -05:00 committed by GitHub
parent 3945528d40
commit a7f808afb6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 308 additions and 366 deletions

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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) {

View file

@ -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 owners 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 owners 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;
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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}")));

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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() {

View file

@ -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
));
}

View file

@ -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));

View file

@ -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));

View file

@ -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 ? ". <i>(You can't look at it.)</i>" : "");
}
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<Card> 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);
}
}

View file

@ -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 ");
}