implement [BLB] Dragonhawk, Festival of Embers, Jackdaw Savior, Infamous Cruelclaw (#12906)

* [BLB] Dragonhawk, Fate's Tempest

* [BLB] Festival of Embers

* [BLB] Jackdaw Savior + test

* [BLB] The Infamous Cruelclaw

* Dragonhawk changes

* Create generic GraveyardFromAnywhereExileReplacementEffect

* Hide Cruelclaw Menace reminder text

* fix style errors
This commit is contained in:
ssk97 2024-10-12 11:07:52 -07:00 committed by GitHub
parent 9fd3f91388
commit be745cb096
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 675 additions and 443 deletions

View file

@ -0,0 +1,127 @@
package mage.cards.d;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.mageobject.PowerPredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.targetpointer.FixedTargets;
import java.util.Set;
import java.util.UUID;
/**
* @author notgreat
*/
public final class DragonhawkFatesTempest extends CardImpl {
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures you control with power 4 or greater");
static {
filter.add(new PowerPredicate(ComparisonType.OR_GREATER, 4));
}
public DragonhawkFatesTempest(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.BIRD);
this.subtype.add(SubType.DRAGON);
this.power = new MageInt(5);
this.toughness = new MageInt(5);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever Dragonhawk enters or attacks, exile the top X cards of your library, where X is the number of creatures you control with power 4 or greater. You may play those cards until your next end step.
// At the beginning of your next end step, Dragonhawk deals 2 damage to each opponent for each of those cards that are still exiled.
this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new DragonhawkExileEffect(
new PermanentsOnBattlefieldCount(filter, null), Duration.UntilYourNextEndStep)
.withTextOptions("those cards", true)));
}
private DragonhawkFatesTempest(final DragonhawkFatesTempest card) {
super(card);
}
@Override
public DragonhawkFatesTempest copy() {
return new DragonhawkFatesTempest(this);
}
}
// Copied from ExileTopXMayPlayUntilEffect but with addDelayedTriggeredAbility
class DragonhawkExileEffect extends ExileTopXMayPlayUntilEffect {
public DragonhawkExileEffect(DynamicValue amount, Duration duration) {
super(amount, duration);
staticText += ". At the beginning of your next end step, " + DragonhawkFatesTempestDamageEffect.STATIC_TEXT;
}
private DragonhawkExileEffect(final DragonhawkExileEffect effect) {
super(effect);
}
@Override
public DragonhawkExileEffect copy() {
return new DragonhawkExileEffect(this);
}
protected void effectCards(Game game, Ability source, Set<Card> cards) {
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
new DragonhawkFatesTempestDamageEffect(new FixedTargets(cards, game)), TargetController.YOU), source);
}
}
class DragonhawkFatesTempestDamageEffect extends OneShotEffect {
FixedTargets cards;
public static String STATIC_TEXT = "{this} deals 2 damage to each opponent for each of those cards that are still exiled";
DragonhawkFatesTempestDamageEffect(FixedTargets cards) {
super(Outcome.Benefit);
this.staticText = STATIC_TEXT;
this.cards = cards;
}
private DragonhawkFatesTempestDamageEffect(final DragonhawkFatesTempestDamageEffect effect) {
super(effect);
cards = effect.cards;
}
@Override
public DragonhawkFatesTempestDamageEffect copy() {
return new DragonhawkFatesTempestDamageEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return false;
}
int count = cards.getTargets(game, source).size(); //Automatically filters out moved cards
if (count < 1) {
return false;
}
for (UUID playerId : game.getOpponents(source.getControllerId())) {
player = game.getPlayer(playerId);
if (playerId == null) {
continue;
}
player.damage(count * 2, source.getSourceId(), source, game);
}
return true;
}
}

View file

@ -1,22 +1,16 @@
package mage.cards.d;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.Card;
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
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.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.filter.StaticFilters;
import java.util.UUID;
/**
*
@ -33,7 +27,7 @@ public final class DryadMilitant extends CardImpl {
this.toughness = new MageInt(1);
// If an instant or sorcery card would be put into a graveyard from anywhere, exile it instead.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DryadMilitantReplacementEffect()));
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, false)));
}
private DryadMilitant(final DryadMilitant card) {
@ -45,42 +39,3 @@ public final class DryadMilitant extends CardImpl {
return new DryadMilitant(this);
}
}
class DryadMilitantReplacementEffect extends ReplacementEffectImpl {
DryadMilitantReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.Exile);
staticText = "If an instant or sorcery card would be put into a graveyard from anywhere, exile it instead";
}
private DryadMilitantReplacementEffect(final DryadMilitantReplacementEffect effect) {
super(effect);
}
@Override
public DryadMilitantReplacementEffect copy() {
return new DryadMilitantReplacementEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
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 (((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD) {
Card card = game.getCard(event.getTargetId());
if (card != null && (card.isSorcery(game) || card.isInstant(game))) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,97 @@
package mage.cards.f;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.CostsImpl;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.abilities.effects.common.SacrificeSourceEffect;
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
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 notgreat
*/
public final class FestivalOfEmbers extends CardImpl {
public FestivalOfEmbers(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}");
// During your turn, you may cast instant and sorcery spells from your graveyard by paying 1 life in addition to their other costs.
this.addAbility(new SimpleStaticAbility(new FestivalOfEmbersCastEffect()));
// If a card or token would be put into your graveyard from anywhere, exile it instead.
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(true, true)));
// {1}{R}: Sacrifice Festival of Embers.
this.addAbility(new SimpleActivatedAbility(new SacrificeSourceEffect(), new ManaCostsImpl<>("{1}{R}")));
}
private FestivalOfEmbers(final FestivalOfEmbers card) {
super(card);
}
@Override
public FestivalOfEmbers copy() {
return new FestivalOfEmbers(this);
}
}
//Based on Osteomancer Adept
class FestivalOfEmbersCastEffect extends AsThoughEffectImpl {
FestivalOfEmbersCastEffect() {
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.AIDontUseIt);
staticText = "During your turn, you may cast instant and sorcery spells from your graveyard by paying 1 life in addition to their other costs.";
}
private FestivalOfEmbersCastEffect(final FestivalOfEmbersCastEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
return true;
}
@Override
public FestivalOfEmbersCastEffect copy() {
return new FestivalOfEmbersCastEffect(this);
}
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (!source.isControlledBy(affectedControllerId)) {
return false;
}
Card card = game.getCard(objectId);
Player player = game.getPlayer(affectedControllerId);
if (card == null
|| player == null
|| !game.getActivePlayerId().equals(affectedControllerId)
|| !card.isOwnedBy(affectedControllerId)
|| !card.isInstantOrSorcery(game)
|| !game.getState().getZone(objectId).match(Zone.GRAVEYARD)) {
return false;
}
Costs<Cost> newCosts = new CostsImpl<>();
newCosts.addAll(card.getSpellAbility().getCosts());
newCosts.add(new PayLifeCost(1));
player.setCastSourceIdWithAlternateMana(
card.getId(), card.getManaCost(), newCosts
);
return true;
}
}

View file

@ -1,10 +1,10 @@
package mage.cards.f;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
@ -14,12 +14,11 @@ import mage.constants.Outcome;
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;
import mage.players.Player;
import mage.target.common.TargetCardInYourGraveyard;
import java.util.UUID;
/**
*
* @author Quercitron
@ -30,9 +29,9 @@ public final class ForbiddenCrypt extends CardImpl {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{B}{B}");
// If you would draw a card, return a card from your graveyard to your hand instead. If you can't, you lose the game.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ForbiddenCryptDrawCardReplacementEffect()));
this.addAbility(new SimpleStaticAbility(new ForbiddenCryptDrawCardReplacementEffect()));
// If a card would be put into your graveyard from anywhere, exile that card instead.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ForbiddenCryptPutIntoYourGraveyardReplacementEffect()));
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(true, false)));
}
private ForbiddenCrypt(final ForbiddenCrypt card) {
@ -96,47 +95,4 @@ class ForbiddenCryptDrawCardReplacementEffect extends ReplacementEffectImpl {
return event.getPlayerId().equals(source.getControllerId());
}
}
class ForbiddenCryptPutIntoYourGraveyardReplacementEffect extends ReplacementEffectImpl {
ForbiddenCryptPutIntoYourGraveyardReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
this.staticText = "If a card would be put into your graveyard from anywhere, exile that card instead";
}
private ForbiddenCryptPutIntoYourGraveyardReplacementEffect(final ForbiddenCryptPutIntoYourGraveyardReplacementEffect effect) {
super(effect);
}
@Override
public ForbiddenCryptPutIntoYourGraveyardReplacementEffect copy() {
return new ForbiddenCryptPutIntoYourGraveyardReplacementEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
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 (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) {
Card card = game.getCard(event.getTargetId());
if (card != null && card.isOwnedBy(source.getControllerId())) {
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
if (!(permanent instanceof PermanentToken)) {
return true;
}
}
}
return false;
}
}
}

View file

@ -1,23 +1,19 @@
package mage.cards.g;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
import mage.abilities.keyword.SuspendAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author weirddan455
@ -36,7 +32,7 @@ public final class GaeasWill extends CardImpl {
this.getSpellAbility().addEffect(new GaeasWillGraveyardEffect());
// If a card would be put into your graveyard from anywhere this turn, exile that card instead.
this.getSpellAbility().addEffect(new GaeassWillReplacementEffect());
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(Duration.EndOfTurn)));
}
private GaeasWill(final GaeasWill card) {
@ -79,45 +75,3 @@ class GaeasWillGraveyardEffect extends ContinuousEffectImpl {
return false;
}
}
class GaeassWillReplacementEffect extends ReplacementEffectImpl {
GaeassWillReplacementEffect() {
super(Duration.EndOfTurn, Outcome.Detriment);
this.staticText = "<br>If a card would be put into your graveyard from anywhere this turn, exile that card instead";
}
private GaeassWillReplacementEffect(final GaeassWillReplacementEffect effect) {
super(effect);
}
@Override
public GaeassWillReplacementEffect copy() {
return new GaeassWillReplacementEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
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 (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) {
Card card = game.getCard(event.getTargetId());
if (card != null && card.isOwnedBy(source.getControllerId())) {
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
if (!(permanent instanceof PermanentToken)) {
return true;
}
}
}
return false;
}
}

View file

@ -0,0 +1,91 @@
package mage.cards.j;
import mage.MageInt;
import mage.abilities.common.DiesThisOrAnotherTriggeredAbility;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
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.FilterCreatureCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.filter.predicate.mageobject.MageObjectReferencePredicate;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.target.common.TargetCardInYourGraveyard;
import java.util.UUID;
/**
* @author notgreat
*/
public final class JackdawSavior extends CardImpl {
public JackdawSavior(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
this.subtype.add(SubType.BIRD);
this.subtype.add(SubType.CLERIC);
this.power = new MageInt(3);
this.toughness = new MageInt(1);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever Jackdaw Savior or another creature you control with flying dies, return another target creature card with lesser mana value from your graveyard to the battlefield.
this.addAbility(new JackdawSaviorDiesThisOrAnotherTriggeredAbility());
}
private JackdawSavior(final JackdawSavior card) {
super(card);
}
@Override
public JackdawSavior copy() {
return new JackdawSavior(this);
}
}
class JackdawSaviorDiesThisOrAnotherTriggeredAbility extends DiesThisOrAnotherTriggeredAbility {
private static final FilterControlledCreaturePermanent flyingFilter = new FilterControlledCreaturePermanent("creature you control with flying");
static {
flyingFilter.add(new AbilityPredicate(FlyingAbility.class));
}
public JackdawSaviorDiesThisOrAnotherTriggeredAbility() {
super(new ReturnFromGraveyardToBattlefieldTargetEffect().setText(
"return another target creature card with lesser mana value from your graveyard to the battlefield"),
false, flyingFilter);
}
protected JackdawSaviorDiesThisOrAnotherTriggeredAbility(final JackdawSaviorDiesThisOrAnotherTriggeredAbility ability) {
super(ability);
}
@Override
public JackdawSaviorDiesThisOrAnotherTriggeredAbility copy() {
return new JackdawSaviorDiesThisOrAnotherTriggeredAbility(this);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (super.checkTrigger(event, game)) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
FilterCard filter = new FilterCreatureCard();
filter.add(Predicates.not(new MageObjectReferencePredicate(zEvent.getTargetId(), game)));
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, zEvent.getTarget().getManaValue()));
filter.setMessage("target creature card other than "+zEvent.getTarget().getLogName()+" with mana value less than "+zEvent.getTarget().getManaValue());
this.getTargets().clear();
this.addTarget(new TargetCardInYourGraveyard(filter));
return true;
}
return false;
}
}

View file

@ -1,7 +1,6 @@
package mage.cards.m;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
@ -9,24 +8,15 @@ import mage.abilities.costs.common.ExileSourceCost;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.Card;
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
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.Zone;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author fireshoes
@ -44,7 +34,7 @@ public final class MagusOfTheWill extends CardImpl {
// {2}{B}, {T}, Exile Magus of the Will: Until end of turn, you may play cards from your graveyard.
// If a card would be put into your graveyard from anywhere else this turn, exile that card instead.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CanPlayCardsFromGraveyardEffect(), new ManaCostsImpl<>("{2}{B}"));
ability.addEffect(new MagusOfTheWillReplacementEffect());
ability.addEffect(new GraveyardFromAnywhereExileReplacementEffect(Duration.EndOfTurn));
ability.addCost(new TapSourceCost());
ability.addCost(new ExileSourceCost());
this.addAbility(ability);
@ -91,46 +81,3 @@ class CanPlayCardsFromGraveyardEffect extends ContinuousEffectImpl {
}
}
class MagusOfTheWillReplacementEffect extends ReplacementEffectImpl {
MagusOfTheWillReplacementEffect() {
super(Duration.EndOfTurn, Outcome.Detriment);
this.staticText = "If a card would be put into your graveyard from anywhere else this turn, exile that card instead";
}
private MagusOfTheWillReplacementEffect(final MagusOfTheWillReplacementEffect effect) {
super(effect);
}
@Override
public MagusOfTheWillReplacementEffect copy() {
return new MagusOfTheWillReplacementEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
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 (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) {
Card card = game.getCard(event.getTargetId());
if (card != null && card.isOwnedBy(source.getControllerId())) {
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
if (!(permanent instanceof PermanentToken)) {
return true;
}
}
}
return false;
}
}

View file

@ -6,18 +6,13 @@ import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.PayLifeCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.SkipDrawStepEffect;
import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect;
import mage.cards.Card;
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import java.util.UUID;
@ -50,7 +45,7 @@ public final class Necrodominance extends CardImpl {
));
// If a card or token would be put into your graveyard from anywhere, exile it instead.
this.addAbility(new SimpleStaticAbility(new NecrodominanceReplacementEffect()));
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(true, true)));
}
private Necrodominance(final Necrodominance card) {
@ -95,50 +90,4 @@ class NecrodominanceEffect extends OneShotEffect {
return true;
}
}
// Inspired by [Rest in Peace] and [Wheel of Sun and Moon]
class NecrodominanceReplacementEffect extends ReplacementEffectImpl {
NecrodominanceReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.Exile);
staticText = "If a card or token would be put into your graveyard from anywhere, exile it instead";
}
private NecrodominanceReplacementEffect(final NecrodominanceReplacementEffect effect) {
super(effect);
}
@Override
public NecrodominanceReplacementEffect copy() {
return new NecrodominanceReplacementEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
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;
if (zEvent.getToZone() != Zone.GRAVEYARD) {
return false;
}
Card card = game.getCard(event.getTargetId());
if (card != null && card.isOwnedBy(source.getControllerId())) {
return true;
}
Permanent token = game.getPermanent(event.getTargetId());
if (token != null && token instanceof PermanentToken && token.isOwnedBy(source.getControllerId())) {
return true;
}
return false;
}
}
}

View file

@ -1,25 +1,15 @@
package mage.cards.r;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect;
import mage.cards.Card;
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
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.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.players.Player;
import java.util.UUID;
/**
* @author LevelX2
@ -49,7 +39,7 @@ public final class RestInPeace extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new ExileGraveyardAllPlayersEffect()));
// If a card or token would be put into a graveyard from anywhere, exile it instead.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new RestInPeaceReplacementEffect()));
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(false, true)));
}
private RestInPeace(final RestInPeace card) {
@ -61,36 +51,3 @@ public final class RestInPeace extends CardImpl {
return new RestInPeace(this);
}
}
class RestInPeaceReplacementEffect extends ReplacementEffectImpl {
RestInPeaceReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.Exile);
staticText = "If a card or token would be put into a graveyard from anywhere, exile it instead";
}
private RestInPeaceReplacementEffect(final RestInPeaceReplacementEffect effect) {
super(effect);
}
@Override
public RestInPeaceReplacementEffect copy() {
return new RestInPeaceReplacementEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
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) {
return ((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD;
}
}

View file

@ -0,0 +1,118 @@
package mage.cards.t;
import mage.ApprovingObject;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.Costs;
import mage.abilities.costs.CostsImpl;
import mage.abilities.costs.common.DiscardCardCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.MenaceAbility;
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.util.CardUtil;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author notgreat
*/
public final class TheInfamousCruelclaw extends CardImpl {
public TheInfamousCruelclaw(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{R}");
this.supertype.add(SuperType.LEGENDARY);
this.subtype.add(SubType.WEASEL);
this.subtype.add(SubType.MERCENARY);
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Menace
this.addAbility(new MenaceAbility(false));
// Whenever The Infamous Cruelclaw deals combat damage to a player, exile cards from the top of your library until you exile a nonland card. You may cast that card by discarding a card rather than paying its mana cost.
this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new InfamousCruelclawEffect()));
}
private TheInfamousCruelclaw(final TheInfamousCruelclaw card) {
super(card);
}
@Override
public TheInfamousCruelclaw copy() {
return new TheInfamousCruelclaw(this);
}
}
//Based on Amped Raptor
class InfamousCruelclawEffect extends OneShotEffect {
InfamousCruelclawEffect() {
super(Outcome.PlayForFree);
staticText = "exile cards from the top of your library until you exile a nonland card. "
+ "You may cast that card by discarding a card rather than paying its mana cost.";
}
private InfamousCruelclawEffect(final InfamousCruelclawEffect effect) {
super(effect);
}
@Override
public InfamousCruelclawEffect copy() {
return new InfamousCruelclawEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null || !controller.getLibrary().hasCards()) {
return false;
}
for (Card card : controller.getLibrary().getCards(game)) {
controller.moveCards(card, Zone.EXILED, source, game);
if (!card.isLand(game)) {
List<Card> castableComponents = CardUtil.getCastableComponents(card, null, source, controller, game, null, false);
if (castableComponents.isEmpty()) {
break;
}
String partsInfo = castableComponents
.stream()
.map(MageObject::getLogName)
.collect(Collectors.joining(" or "));
if (!controller.chooseUse(Outcome.PlayForFree, "Cast spell by discarding a card instead of mana (" + partsInfo + ")?", source, game)) {
break;
}
castableComponents.forEach(partCard -> game.getState().setValue("PlayFromNotOwnHandZone" + partCard.getId(), Boolean.TRUE));
SpellAbility chosenAbility = controller.chooseAbilityForCast(card, game, true);
if (chosenAbility != null) {
Card faceCard = game.getCard(chosenAbility.getSourceId());
if (faceCard != null) {
// discard instead of mana cost
Costs<Cost> newCosts = new CostsImpl<>();
newCosts.add(new DiscardCardCost());
newCosts.addAll(chosenAbility.getCosts());
controller.setCastSourceIdWithAlternateMana(faceCard.getId(), null, newCosts);
controller.cast(
chosenAbility, game, true,
new ApprovingObject(source, game)
);
}
}
castableComponents.forEach(partCard -> game.getState().setValue("PlayFromNotOwnHandZone" + partCard.getId(), null));
break;
}
}
return true;
}
}

View file

@ -1,29 +1,19 @@
package mage.cards.y;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.continuous.CantCastMoreThanOneSpellEffect;
import mage.cards.Card;
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
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.TargetController;
import mage.constants.Zone;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author LevelX2
@ -34,11 +24,11 @@ public final class YawgmothsAgenda extends CardImpl {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{B}{B}");
// You can't cast more than one spell each turn.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantCastMoreThanOneSpellEffect(TargetController.YOU)));
this.addAbility(new SimpleStaticAbility(new CantCastMoreThanOneSpellEffect(TargetController.YOU)));
// You may play cards from your graveyard.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new YawgmothsAgendaCanPlayCardsFromGraveyardEffect()));
this.addAbility(new SimpleStaticAbility(new YawgmothsAgendaCanPlayCardsFromGraveyardEffect()));
// If a card would be put into your graveyard from anywhere, exile it instead.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new YawgmothsAgendaReplacementEffect()));
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(true, false)));
}
private YawgmothsAgenda(final YawgmothsAgenda card) {
@ -80,48 +70,4 @@ class YawgmothsAgendaCanPlayCardsFromGraveyardEffect extends ContinuousEffectImp
}
return false;
}
}
class YawgmothsAgendaReplacementEffect extends ReplacementEffectImpl {
YawgmothsAgendaReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
this.staticText = "If a card would be put into your graveyard from anywhere, exile it instead";
}
private YawgmothsAgendaReplacementEffect(final YawgmothsAgendaReplacementEffect effect) {
super(effect);
}
@Override
public YawgmothsAgendaReplacementEffect copy() {
return new YawgmothsAgendaReplacementEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
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 (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) {
Card card = game.getCard(event.getTargetId());
if (card != null && card.isOwnedBy(source.getControllerId())) {
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
if (!(permanent instanceof PermanentToken)) {
return true;
}
}
}
return false;
}
}

View file

@ -1,27 +1,17 @@
package mage.cards.y;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.Card;
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
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.Zone;
import mage.constants.*;
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.players.Player;
import java.util.UUID;
/**
*
* @author LevelX2
@ -35,7 +25,7 @@ public final class YawgmothsWill extends CardImpl {
this.getSpellAbility().addEffect(new CanPlayCardsFromGraveyardEffect());
// If a card would be put into your graveyard from anywhere this turn, exile that card instead.
this.getSpellAbility().addEffect(new YawgmothsWillReplacementEffect());
this.getSpellAbility().addEffect(new GraveyardFromAnywhereExileReplacementEffect(Duration.EndOfTurn).concatBy("<br>"));
}
private YawgmothsWill(final YawgmothsWill card) {
@ -79,46 +69,3 @@ class CanPlayCardsFromGraveyardEffect extends ContinuousEffectImpl {
}
}
class YawgmothsWillReplacementEffect extends ReplacementEffectImpl {
YawgmothsWillReplacementEffect() {
super(Duration.EndOfTurn, Outcome.Detriment);
this.staticText = "If a card would be put into your graveyard from anywhere this turn, exile that card instead";
}
private YawgmothsWillReplacementEffect(final YawgmothsWillReplacementEffect effect) {
super(effect);
}
@Override
public YawgmothsWillReplacementEffect copy() {
return new YawgmothsWillReplacementEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
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 (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) {
Card card = game.getCard(event.getTargetId());
if (card != null && card.isOwnedBy(source.getControllerId())) {
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
if (!(permanent instanceof PermanentToken)) {
return true;
}
}
}
return false;
}
}

View file

@ -80,6 +80,7 @@ public final class Bloomburrow extends ExpansionSet {
cards.add(new SetCardInfo("Diresight", 91, Rarity.COMMON, mage.cards.d.Diresight.class));
cards.add(new SetCardInfo("Dour Port-Mage", 47, Rarity.RARE, mage.cards.d.DourPortMage.class));
cards.add(new SetCardInfo("Downwind Ambusher", 92, Rarity.UNCOMMON, mage.cards.d.DownwindAmbusher.class));
cards.add(new SetCardInfo("Dragonhawk, Fate's Tempest", 132, Rarity.MYTHIC, mage.cards.d.DragonhawkFatesTempest.class));
cards.add(new SetCardInfo("Dreamdew Entrancer", 211, Rarity.RARE, mage.cards.d.DreamdewEntrancer.class));
cards.add(new SetCardInfo("Driftgloom Coyote", 11, Rarity.UNCOMMON, mage.cards.d.DriftgloomCoyote.class));
cards.add(new SetCardInfo("Druid of the Spade", 170, Rarity.COMMON, mage.cards.d.DruidOfTheSpade.class));
@ -94,6 +95,7 @@ public final class Bloomburrow extends ExpansionSet {
cards.add(new SetCardInfo("Fecund Greenshell", 362, Rarity.RARE, mage.cards.f.FecundGreenshell.class, NON_FULL_USE_VARIOUS));
cards.add(new SetCardInfo("Feed the Cycle", 94, Rarity.UNCOMMON, mage.cards.f.FeedTheCycle.class));
cards.add(new SetCardInfo("Fell", 95, Rarity.UNCOMMON, mage.cards.f.Fell.class));
cards.add(new SetCardInfo("Festival of Embers", 134, Rarity.RARE, mage.cards.f.FestivalOfEmbers.class));
cards.add(new SetCardInfo("Finch Formation", 50, Rarity.COMMON, mage.cards.f.FinchFormation.class));
cards.add(new SetCardInfo("Finneas, Ace Archer", 212, Rarity.RARE, mage.cards.f.FinneasAceArcher.class));
cards.add(new SetCardInfo("Fireglass Mentor", 213, Rarity.UNCOMMON, mage.cards.f.FireglassMentor.class));
@ -135,6 +137,7 @@ public final class Bloomburrow extends ExpansionSet {
cards.add(new SetCardInfo("Intrepid Rabbit", 17, Rarity.COMMON, mage.cards.i.IntrepidRabbit.class));
cards.add(new SetCardInfo("Iridescent Vinelasher", 99, Rarity.RARE, mage.cards.i.IridescentVinelasher.class));
cards.add(new SetCardInfo("Island", 266, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS));
cards.add(new SetCardInfo("Jackdaw Savior", 18, Rarity.RARE, mage.cards.j.JackdawSavior.class));
cards.add(new SetCardInfo("Jolly Gerbils", 19, Rarity.UNCOMMON, mage.cards.j.JollyGerbils.class));
cards.add(new SetCardInfo("Junkblade Bruiser", 220, Rarity.COMMON, mage.cards.j.JunkbladeBruiser.class));
cards.add(new SetCardInfo("Kastral, the Windcrested", 221, Rarity.RARE, mage.cards.k.KastralTheWindcrested.class));
@ -259,6 +262,7 @@ public final class Bloomburrow extends ExpansionSet {
cards.add(new SetCardInfo("Teapot Slinger", 157, Rarity.UNCOMMON, mage.cards.t.TeapotSlinger.class));
cards.add(new SetCardInfo("Tempest Angler", 235, Rarity.COMMON, mage.cards.t.TempestAngler.class));
cards.add(new SetCardInfo("Tender Wildguide", 196, Rarity.RARE, mage.cards.t.TenderWildguide.class));
cards.add(new SetCardInfo("The Infamous Cruelclaw", 219, Rarity.MYTHIC, mage.cards.t.TheInfamousCruelclaw.class));
cards.add(new SetCardInfo("Thieving Otter", 390, Rarity.COMMON, mage.cards.t.ThievingOtter.class));
cards.add(new SetCardInfo("Thistledown Players", 35, Rarity.COMMON, mage.cards.t.ThistledownPlayers.class));
cards.add(new SetCardInfo("Thornplate Intimidator", 117, Rarity.COMMON, mage.cards.t.ThornplateIntimidator.class));

View file

@ -0,0 +1,90 @@
package org.mage.test.cards.single.blb;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author notgreat
*/
public class JackdawSaviorTest extends CardTestPlayerBase {
/**
* {@link mage.cards.j.JackdawSavior Jackdaw Savior} {2}{W}
* Creature Bird Cleric
* Flying
* Whenever Jackdaw Savior or another creature you control with flying dies,
* return another target creature card with lesser mana value from your graveyard to the battlefield.
* <p>
* This card is unusual in that "another" here refers to "other than the creature that just died",
* which xmage does not natively support.
*/
@Test
public void test_Simultaneous() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Jackdaw Savior"); //MV = 3, flying
addCard(Zone.BATTLEFIELD, playerA, "Air Elemental"); //MV = 5, flying
addCard(Zone.BATTLEFIELD, playerA, "Memnite"); //MV = 1
addCard(Zone.HAND, playerA, "Damnation");
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Damnation");
setChoice(playerA, ""); //Order triggers
addTarget(playerA, "Jackdaw Savior");
addTarget(playerA, "Memnite");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, 2); //Air Elemental+Damnation
assertPermanentCount(playerA, "Jackdaw Savior", 1);
assertPermanentCount(playerA, "Memnite", 1);
}
@Test
public void test_Clones() {
setStrictChooseMode(true);
addCard(Zone.BATTLEFIELD, playerA, "Jackdaw Savior"); //MV = 3, flying
addCard(Zone.BATTLEFIELD, playerA, "Air Elemental"); //MV = 5, flying
addCard(Zone.BATTLEFIELD, playerA, "Spidersilk Armor"); //+0/+1 to all
addCard(Zone.HAND, playerA, "Phantasmal Image", 2); //MV = 2, clone
addCard(Zone.HAND, playerA, "Murder", 2);
addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 10);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phantasmal Image");
setChoice(playerA, true);
setChoice(playerA, "Air Elemental");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder");
addTarget(playerA, "Air Elemental[only copy]");
// Can't target itself, no valid targets
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phantasmal Image");
setChoice(playerA, true);
setChoice(playerA, "Air Elemental");
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder");
addTarget(playerA, "Air Elemental[only copy]");
addTarget(playerA, "Phantasmal Image"); // Target the previous one
setChoice(playerA, false); //Don't copy, stay on battlefield as 0/1
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, "Murder", 2);
assertGraveyardCount(playerA, "Phantasmal Image", 1);
assertPermanentCount(playerA, "Phantasmal Image", 1);
assertPermanentCount(playerA, "Jackdaw Savior", 1);
assertPermanentCount(playerA, "Air Elemental", 1);
}
}

View file

@ -56,19 +56,12 @@ public class DiesThisOrAnotherTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.isDiesEvent()) {
if (zEvent.getTarget() != null) {
if (!applyFilterOnSource && zEvent.getTarget().getId().equals(this.getSourceId())) {
// TODO: remove this workaround for Basri's Lieutenant
return true;
} else {
if (filter.match(zEvent.getTarget(), getControllerId(), this, game)) {
return true;
}
}
}
if (!zEvent.isDiesEvent() || zEvent.getTarget() == null) {
return false;
}
return false;
// TODO: remove applyFilterOnSource workaround for Basri's Lieutenant
return ((!applyFilterOnSource && zEvent.getTarget().getId().equals(this.getSourceId()))
|| filter.match(zEvent.getTarget(), getControllerId(), this, game));
}
@Override

View file

@ -32,7 +32,7 @@ public class ExileTopXMayPlayUntilEffect extends OneShotEffect {
makeText(amount.toString().equals("1") ? "that card" : "those cards", duration == Duration.EndOfTurn);
}
private ExileTopXMayPlayUntilEffect(final ExileTopXMayPlayUntilEffect effect) {
protected ExileTopXMayPlayUntilEffect(final ExileTopXMayPlayUntilEffect effect) {
super(effect);
this.amount = effect.amount.copy();
this.duration = effect.duration;
@ -60,10 +60,15 @@ public class ExileTopXMayPlayUntilEffect extends OneShotEffect {
if (!cards.isEmpty()) {
game.addEffect(new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, duration)
.setTargetPointer(new FixedTargets(cards, game)), source);
effectCards(game, source, cards);
}
return true;
}
protected void effectCards(Game game, Ability source, Set<Card> cards) {
//Do nothing, used for derived classes
}
/**
* [Until end of turn, ] you may play [refCardText] [this turn]
*/
@ -76,7 +81,11 @@ public class ExileTopXMayPlayUntilEffect extends OneShotEffect {
String text = "exile the top ";
boolean singular = amount.toString().equals("1");
text += singular ? "card" : CardUtil.numberToText(amount.toString()) + " cards";
text += " of your library. ";
if (amount.toString().equals("X")) {
text += " of your library, where X is " + amount.getMessage() + ". ";
} else {
text += " of your library. ";
}
if (durationRuleAtEnd) {
text += "You may play " + refCardText + ' ' + (duration == Duration.EndOfTurn ? "this turn" : duration.toString());
} else {

View file

@ -0,0 +1,92 @@
package mage.abilities.effects.common.replacement;
import mage.abilities.Ability;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.Card;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentToken;
import mage.util.CardUtil;
/**
* @author notgreat
*/
public class GraveyardFromAnywhereExileReplacementEffect extends ReplacementEffectImpl {
private final FilterCard filter;
private final boolean onlyYou;
private final boolean tokens;
public GraveyardFromAnywhereExileReplacementEffect(FilterCard filter, boolean onlyYou) {
this(Duration.WhileOnBattlefield, filter, onlyYou, false);
}
public GraveyardFromAnywhereExileReplacementEffect(boolean onlyYou, boolean tokens) {
this(Duration.WhileOnBattlefield, StaticFilters.FILTER_CARD_A, onlyYou, tokens);
}
public GraveyardFromAnywhereExileReplacementEffect(Duration duration) {
this(duration, StaticFilters.FILTER_CARD_A, true, false);
}
protected GraveyardFromAnywhereExileReplacementEffect(Duration duration, FilterCard filter, boolean onlyYou, boolean tokens) {
super(duration, Outcome.Exile);
this.filter = filter;
this.onlyYou = onlyYou;
this.tokens = tokens;
this.setText();
}
private GraveyardFromAnywhereExileReplacementEffect(final GraveyardFromAnywhereExileReplacementEffect effect) {
super(effect);
this.filter = effect.filter;
this.onlyYou = effect.onlyYou;
this.tokens = effect.tokens;
}
private void setText() {
this.staticText = "If " + CardUtil.addArticle(filter.getMessage()) + (tokens ? " or token" : "")
+ " would be put into " + (onlyYou ? "your" : "a") + " graveyard from anywhere"
+ (duration == Duration.EndOfTurn ? " this turn" : "") + ", exile "
+ ((filter == StaticFilters.FILTER_CARD_A && !tokens) ? "that card" : "it") + " instead";
}
@Override
public GraveyardFromAnywhereExileReplacementEffect copy() {
return new GraveyardFromAnywhereExileReplacementEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
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;
if (zEvent.getToZone() != Zone.GRAVEYARD) {
return false;
}
Card card = game.getCard(event.getTargetId());
if (card != null && (!onlyYou || card.isOwnedBy(source.getControllerId())) && (filter == null || filter.match(card, game))) {
return true;
}
Permanent token = game.getPermanent(event.getTargetId());
if (tokens && (token instanceof PermanentToken && (!onlyYou || token.isOwnedBy(source.getControllerId())))) {
return true;
}
return false;
}
}