mirror of
https://github.com/correl/mage.git
synced 2024-11-14 11:09:31 +00:00
Added Saruman of Many Colors (#10434)
* Added Saruman of Many Colors Borrowed functionality in CastSecondSpellTriggeredAbility from magefree/mage#10433 Added new helper class ExileTargetCardCopyAndCastEffect for common effect * Updated FlawlessForgery to use new ExileTargetCardCopyAndCastEffect * Fixed overriden Effect copy functions * Expanded ExileTargetCardCopyAndCastEffect Added ability for non-free spells * Removed filter lock * De-duplicated exile and cast effects * Fixed demilich
This commit is contained in:
parent
0b2f582d84
commit
80cb439862
12 changed files with 454 additions and 358 deletions
|
@ -1,26 +1,23 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.condition.common.CastFromEverywhereSourceCondition;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect;
|
||||
import mage.abilities.keyword.PrototypeAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterInstantOrSorceryCard;
|
||||
import mage.filter.predicate.ObjectSourcePlayer;
|
||||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
|
||||
import java.util.Objects;
|
||||
|
@ -52,7 +49,7 @@ public final class ArcaneProxy extends CardImpl {
|
|||
|
||||
// When Arcane Proxy enters the battlefield, if you cast it, exile target instant or sorcery card with mana value less than or equal to Arcane Proxy's power from your graveyard. Copy that card. You may cast the copy without paying its mana cost.
|
||||
Ability ability = new ConditionalInterveningIfTriggeredAbility(
|
||||
new EntersBattlefieldTriggeredAbility(new ArcaneProxyEffect()),
|
||||
new EntersBattlefieldTriggeredAbility(new ExileTargetCardCopyAndCastEffect(false)),
|
||||
CastFromEverywhereSourceCondition.instance, "When {this} enters the battlefield, " +
|
||||
"if you cast it, exile target instant or sorcery card with mana value less than or equal to {this}'s " +
|
||||
"power from your graveyard. Copy that card. You may cast the copy without paying its mana cost."
|
||||
|
@ -84,39 +81,4 @@ enum ArcaneProxyPredicate implements ObjectSourcePlayerPredicate<Card> {
|
|||
.map(p -> input.getObject().getManaValue() <= p)
|
||||
.orElse(false);
|
||||
}
|
||||
}
|
||||
|
||||
class ArcaneProxyEffect extends OneShotEffect {
|
||||
|
||||
ArcaneProxyEffect() {
|
||||
super(Outcome.Benefit);
|
||||
}
|
||||
|
||||
private ArcaneProxyEffect(final ArcaneProxyEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArcaneProxyEffect copy() {
|
||||
return new ArcaneProxyEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (player == null || card == null || !player.chooseUse(
|
||||
outcome, "Cast a copy of " + card.getName() + '?', source, game
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
Card copiedCard = game.copyCard(card, source, player.getId());
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), Boolean.TRUE);
|
||||
player.cast(
|
||||
player.chooseAbilityForCast(copiedCard, game, false),
|
||||
game, false, new ApprovingObject(source, game)
|
||||
);
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ package mage.cards.d;
|
|||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
|
@ -15,10 +14,9 @@ import mage.abilities.costs.mana.ManaCostsImpl;
|
|||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect;
|
||||
import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.*;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
@ -49,7 +47,8 @@ public final class Demilich extends CardImpl {
|
|||
)).addHint(new ValueHint("Instants and sorceries you've cast this turn", DemilichValue.instance)), new SpellsCastWatcher());
|
||||
|
||||
// Whenever Demilich attacks, exile up to one target instant or sorcery card from your graveyard. Copy it. You may cast the copy.
|
||||
Ability ability = new AttacksTriggeredAbility(new DemilichCopyEffect());
|
||||
Ability ability = new AttacksTriggeredAbility(new ExileTargetCardCopyAndCastEffect(false).setText(
|
||||
"exile up to one target instant or sorcery card from your graveyard. Copy it. You may cast the copy"));
|
||||
ability.addTarget(new TargetCardInYourGraveyard(0, 1, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD));
|
||||
this.addAbility(ability);
|
||||
|
||||
|
@ -95,41 +94,6 @@ enum DemilichValue implements DynamicValue {
|
|||
}
|
||||
}
|
||||
|
||||
class DemilichCopyEffect extends OneShotEffect {
|
||||
|
||||
public DemilichCopyEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "exile up to one target instant or sorcery card from your graveyard. Copy it. You may cast the copy";
|
||||
}
|
||||
|
||||
private DemilichCopyEffect(final DemilichCopyEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DemilichCopyEffect copy() {
|
||||
return new DemilichCopyEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(targetPointer.getFirst(game, source));
|
||||
if (controller == null || card == null) {
|
||||
return false;
|
||||
}
|
||||
controller.moveCards(card, Zone.EXILED, source, game);
|
||||
if (controller.chooseUse(outcome, "Cast copy of " + card.getName() + '?', source, game)) {
|
||||
Card copiedCard = game.copyCard(card, source, controller.getId());
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), Boolean.TRUE);
|
||||
controller.cast(controller.chooseAbilityForCast(copiedCard, game, false),
|
||||
game, false, new ApprovingObject(source, game));
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class DemilichPlayEffect extends AsThoughEffectImpl {
|
||||
|
||||
public DemilichPlayEffect() {
|
||||
|
|
|
@ -1,20 +1,13 @@
|
|||
package mage.cards.f;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect;
|
||||
import mage.abilities.keyword.CasualtyAbility;
|
||||
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.common.FilterInstantOrSorceryCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInGraveyard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -38,7 +31,8 @@ public final class FlawlessForgery extends CardImpl {
|
|||
this.addAbility(new CasualtyAbility(3));
|
||||
|
||||
// Exile target instant or sorcery card from an opponent's graveyard. Copy that card. You may cast the copy without paying its mana cost.
|
||||
this.getSpellAbility().addEffect(new FlawlessForgeryEffect());
|
||||
this.getSpellAbility().addEffect(new ExileTargetCardCopyAndCastEffect(true).setText(
|
||||
"Exile target instant or sorcery card from an opponent's graveyard. Copy that card. You may cast the copy without paying its mana cost."));
|
||||
this.getSpellAbility().addTarget(new TargetCardInGraveyard(filter));
|
||||
}
|
||||
|
||||
|
@ -50,44 +44,4 @@ public final class FlawlessForgery extends CardImpl {
|
|||
public FlawlessForgery copy() {
|
||||
return new FlawlessForgery(this);
|
||||
}
|
||||
}
|
||||
|
||||
class FlawlessForgeryEffect extends OneShotEffect {
|
||||
|
||||
FlawlessForgeryEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "exile target instant or sorcery card from an opponent's graveyard. " +
|
||||
"Copy that card. You may cast the copy without paying its mana cost";
|
||||
}
|
||||
|
||||
private FlawlessForgeryEffect(final FlawlessForgeryEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FlawlessForgeryEffect copy() {
|
||||
return new FlawlessForgeryEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (player == null || card == null) {
|
||||
return false;
|
||||
}
|
||||
player.moveCards(card, Zone.EXILED, source, game);
|
||||
Card cardCopy = game.copyCard(card, source, source.getControllerId());
|
||||
if (!player.chooseUse(outcome, "Cast copy of " +
|
||||
card.getName() + " without paying its mana cost?", source, game)) {
|
||||
return true;
|
||||
}
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + cardCopy.getId(), Boolean.TRUE);
|
||||
player.cast(
|
||||
player.chooseAbilityForCast(cardCopy, game, true),
|
||||
game, true, new ApprovingObject(source, game)
|
||||
);
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + cardCopy.getId(), null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,9 @@
|
|||
package mage.cards.f;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SagaAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect;
|
||||
import mage.abilities.effects.common.MillCardsTargetEffect;
|
||||
import mage.abilities.effects.common.cost.CastFromHandForFreeEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
|
@ -14,8 +11,6 @@ import mage.filter.FilterCard;
|
|||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterInstantOrSorceryCard;
|
||||
import mage.filter.predicate.mageobject.ManaValuePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetPlayer;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
|
||||
|
@ -54,7 +49,8 @@ public final class FoundingTheThirdPath extends CardImpl {
|
|||
// III -- Exile target instant or sorcery card from your graveyard. Copy it. You may cast the copy.
|
||||
sagaAbility.addChapterEffect(
|
||||
this, SagaChapter.CHAPTER_III, SagaChapter.CHAPTER_III,
|
||||
new FoundingTheThirdPathEffect(),
|
||||
new ExileTargetCardCopyAndCastEffect(false).setText(
|
||||
"exile target instant or sorcery card from your graveyard. Copy it. You may cast the copy"),
|
||||
new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD)
|
||||
);
|
||||
this.addAbility(sagaAbility);
|
||||
|
@ -68,42 +64,4 @@ public final class FoundingTheThirdPath extends CardImpl {
|
|||
public FoundingTheThirdPath copy() {
|
||||
return new FoundingTheThirdPath(this);
|
||||
}
|
||||
}
|
||||
|
||||
class FoundingTheThirdPathEffect extends OneShotEffect {
|
||||
|
||||
FoundingTheThirdPathEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "exile target instant or sorcery card from your graveyard. Copy it. You may cast the copy";
|
||||
}
|
||||
|
||||
private FoundingTheThirdPathEffect(final FoundingTheThirdPathEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FoundingTheThirdPathEffect copy() {
|
||||
return new FoundingTheThirdPathEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (controller == null || card == null) {
|
||||
return false;
|
||||
}
|
||||
controller.moveCards(card, Zone.EXILED, source, game);
|
||||
if (!controller.chooseUse(outcome, "Cast copy of " + card.getName() + '?', source, game)) {
|
||||
return true;
|
||||
}
|
||||
Card copiedCard = game.copyCard(card, source, controller.getId());
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), Boolean.TRUE);
|
||||
controller.cast(
|
||||
controller.chooseAbilityForCast(copiedCard, game, false),
|
||||
game, false, new ApprovingObject(source, game)
|
||||
);
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileSpellEffect;
|
||||
import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect;
|
||||
import mage.abilities.keyword.OverloadAbility;
|
||||
import mage.cards.*;
|
||||
import mage.constants.CardType;
|
||||
|
@ -31,7 +32,8 @@ public final class MizzixsMastery extends CardImpl {
|
|||
// Exile target card that's an instant or sorcery from your graveyard.
|
||||
// For each card exiled this way, copy it, and you may cast the copy
|
||||
// without paying its mana cost. Exile Mizzix's Mastery.
|
||||
this.getSpellAbility().addEffect(new MizzixsMasteryEffect());
|
||||
this.getSpellAbility().addEffect(new ExileTargetCardCopyAndCastEffect(true).setText(
|
||||
"Exile target card that's an instant or sorcery from your graveyard. For each card exiled this way, copy it, and you may cast the copy without paying its mana cost. Exile Mizzix's Mastery."));
|
||||
this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(
|
||||
new FilterInstantOrSorceryCard("card that's an instant or sorcery from your graveyard")));
|
||||
this.getSpellAbility().addEffect(new ExileSpellEffect());
|
||||
|
@ -53,48 +55,6 @@ public final class MizzixsMastery extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class MizzixsMasteryEffect extends OneShotEffect {
|
||||
|
||||
public MizzixsMasteryEffect() {
|
||||
super(Outcome.PlayForFree);
|
||||
this.staticText = "Exile target card that's an instant or sorcery from your "
|
||||
+ "graveyard. For each card exiled this way, copy it, and you "
|
||||
+ "may cast the copy without paying its mana cost";
|
||||
}
|
||||
|
||||
public MizzixsMasteryEffect(final MizzixsMasteryEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MizzixsMasteryEffect copy() {
|
||||
return new MizzixsMasteryEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (card != null) {
|
||||
if (controller.moveCards(card, Zone.EXILED, source, game)) {
|
||||
Card cardCopy = game.copyCard(card, source, source.getControllerId());
|
||||
if (cardCopy.getSpellAbility().canChooseTarget(game, controller.getId())
|
||||
&& controller.chooseUse(outcome, "Cast copy of "
|
||||
+ card.getName() + " without paying its mana cost?", source, game)) {
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + cardCopy.getId(), Boolean.TRUE);
|
||||
controller.cast(controller.chooseAbilityForCast(cardCopy, game, true),
|
||||
game, true, new ApprovingObject(source, game));
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + cardCopy.getId(), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class MizzixsMasteryOverloadEffect extends OneShotEffect {
|
||||
|
||||
public MizzixsMasteryOverloadEffect() {
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package mage.cards.n;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
import mage.abilities.keyword.ProwessAbility;
|
||||
import mage.cards.Card;
|
||||
|
@ -20,7 +19,6 @@ import mage.filter.predicate.ObjectSourcePlayer;
|
|||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInGraveyard;
|
||||
|
||||
import java.util.Objects;
|
||||
|
@ -56,7 +54,9 @@ public final class NarsetEnlightenedExile extends CardImpl {
|
|||
).setText("creatures you control have prowess")));
|
||||
|
||||
// Whenever Narset, Enlightened Exile attacks, exile target noncreature, nonland card with mana value less than Narset's power from a graveyard and copy it. You may cast the copy without paying its mana cost.
|
||||
Ability ability = new AttacksTriggeredAbility(new NarsetEnlightenedExileEffect());
|
||||
Ability ability = new AttacksTriggeredAbility(new ExileTargetCardCopyAndCastEffect(true)
|
||||
.setText("exile target noncreature, nonland card with mana value less than {this}'s power " +
|
||||
"from a graveyard and copy it. You may cast the copy without paying its mana cost"));
|
||||
ability.addTarget(new TargetCardInGraveyard(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
@ -84,40 +84,4 @@ enum NarsetEnlightenedExilePredicate implements ObjectSourcePlayerPredicate<Card
|
|||
.map(p -> input.getObject().getManaValue() < p)
|
||||
.orElse(false);
|
||||
}
|
||||
}
|
||||
|
||||
class NarsetEnlightenedExileEffect extends OneShotEffect {
|
||||
|
||||
NarsetEnlightenedExileEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "exile target noncreature, nonland card with mana value less than {this}'s power " +
|
||||
"from a graveyard and copy it. You may cast the copy without paying its mana cost";
|
||||
}
|
||||
|
||||
private NarsetEnlightenedExileEffect(final NarsetEnlightenedExileEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NarsetEnlightenedExileEffect copy() {
|
||||
return new NarsetEnlightenedExileEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (player == null || card == null) {
|
||||
return false;
|
||||
}
|
||||
player.moveCards(card, Zone.EXILED, source, game);
|
||||
Card copiedCard = game.copyCard(card, source, source.getControllerId());
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), Boolean.TRUE);
|
||||
player.cast(
|
||||
player.chooseAbilityForCast(copiedCard, game, true),
|
||||
game, true, new ApprovingObject(source, game)
|
||||
);
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +1,17 @@
|
|||
package mage.cards.n;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect;
|
||||
import mage.abilities.keyword.MenaceAbility;
|
||||
import mage.abilities.keyword.WardAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -50,7 +46,8 @@ public final class NashiMoonsLegacy extends CardImpl {
|
|||
this.addAbility(new WardAbility(new ManaCostsImpl<>("{1}"), false));
|
||||
|
||||
// Whenever Nashi, Moon's Legacy attacks, exile up to one target legendary or Rat card from your graveyard and copy it. You may cast the copy.
|
||||
Ability ability = new AttacksTriggeredAbility(new NashiMoonsLegacyEffect());
|
||||
Ability ability = new AttacksTriggeredAbility(new ExileTargetCardCopyAndCastEffect(false).setText(
|
||||
"exile up to one target legendary or Rat card from your graveyard and copy it. You may cast the copy"));
|
||||
ability.addTarget(new TargetCardInYourGraveyard(0, 1, filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
@ -63,39 +60,4 @@ public final class NashiMoonsLegacy extends CardImpl {
|
|||
public NashiMoonsLegacy copy() {
|
||||
return new NashiMoonsLegacy(this);
|
||||
}
|
||||
}
|
||||
|
||||
class NashiMoonsLegacyEffect extends OneShotEffect {
|
||||
|
||||
NashiMoonsLegacyEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "exile up to one target legendary or Rat card from your graveyard and copy it. You may cast the copy";
|
||||
}
|
||||
|
||||
private NashiMoonsLegacyEffect(final NashiMoonsLegacyEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NashiMoonsLegacyEffect copy() {
|
||||
return new NashiMoonsLegacyEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (player == null || card == null) {
|
||||
return false;
|
||||
}
|
||||
player.moveCards(card, Zone.EXILED, source, game);
|
||||
Card copiedCard = game.copyCard(card, source, player.getId());
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), Boolean.TRUE);
|
||||
player.cast(
|
||||
player.chooseAbilityForCast(copiedCard, game, false),
|
||||
game, false, new ApprovingObject(source, game)
|
||||
);
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +1,16 @@
|
|||
package mage.cards.p;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.common.TapTargetCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileSpellEffect;
|
||||
import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect;
|
||||
import mage.abilities.keyword.ReplicateAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.permanent.TappedPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInGraveyard;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
|
@ -42,7 +35,10 @@ public final class PsionicRitual extends CardImpl {
|
|||
this.addAbility(new ReplicateAbility(new TapTargetCost(new TargetControlledPermanent(filter))));
|
||||
|
||||
// Exile target instant or sorcery card from a graveyard and copy it. You may cast the copy without paying its mana cost.
|
||||
this.getSpellAbility().addEffect(new PsionicRitualEffect());
|
||||
this.getSpellAbility()
|
||||
.addEffect(new ExileTargetCardCopyAndCastEffect(true)
|
||||
.setText("exile target instant or sorcery card from a graveyard " +
|
||||
"and copy it. You may cast the copy without paying its mana cost"));
|
||||
this.getSpellAbility().addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY));
|
||||
|
||||
// Exile Psionic Ritual.
|
||||
|
@ -57,50 +53,4 @@ public final class PsionicRitual extends CardImpl {
|
|||
public PsionicRitual copy() {
|
||||
return new PsionicRitual(this);
|
||||
}
|
||||
}
|
||||
|
||||
class PsionicRitualEffect extends OneShotEffect {
|
||||
|
||||
PsionicRitualEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "exile target instant or sorcery card from a graveyard " +
|
||||
"and copy it. You may cast the copy without paying its mana cost";
|
||||
}
|
||||
|
||||
private PsionicRitualEffect(final PsionicRitualEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsionicRitualEffect copy() {
|
||||
return new PsionicRitualEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (player == null || card == null) {
|
||||
return false;
|
||||
}
|
||||
player.moveCards(card, Zone.EXILED, source, game);
|
||||
Card copiedCard = game.copyCard(card, source, source.getControllerId());
|
||||
if (copiedCard == null) {
|
||||
return false;
|
||||
}
|
||||
game.getExile().add(source.getSourceId(), "", copiedCard);
|
||||
game.getState().setZone(copiedCard.getId(), Zone.EXILED);
|
||||
if (copiedCard.getSpellAbility() == null || !player.chooseUse(
|
||||
outcome, "Cast the copied card without paying mana cost?", source, game
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), Boolean.TRUE);
|
||||
player.cast(
|
||||
player.chooseAbilityForCast(copiedCard, game, true),
|
||||
game, true, new ApprovingObject(source, game)
|
||||
);
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), null);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
125
Mage.Sets/src/mage/cards/s/SarumanOfManyColors.java
Normal file
125
Mage.Sets/src/mage/cards/s/SarumanOfManyColors.java
Normal file
|
@ -0,0 +1,125 @@
|
|||
package mage.cards.s;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.CastSecondSpellTriggeredAbility;
|
||||
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
|
||||
import mage.abilities.costs.common.DiscardTargetCost;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTargetCardCopyAndCastEffect;
|
||||
import mage.abilities.keyword.WardAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterPermanentCard;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.ManaValuePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
import mage.target.common.TargetCardInOpponentsGraveyard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author alexander-novo
|
||||
*/
|
||||
public final class SarumanOfManyColors extends CardImpl {
|
||||
|
||||
static final FilterCard filter = new FilterCard("enchantment, instant, or sorcery card");
|
||||
public static final Predicate<Card> predicate = Predicates.or(
|
||||
CardType.ENCHANTMENT.getPredicate(),
|
||||
CardType.INSTANT.getPredicate(),
|
||||
CardType.SORCERY.getPredicate());
|
||||
|
||||
static {
|
||||
filter.add(predicate);
|
||||
}
|
||||
|
||||
public SarumanOfManyColors(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{3}{W}{U}{B}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.AVATAR);
|
||||
this.subtype.add(SubType.WIZARD);
|
||||
this.power = new MageInt(5);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Ward—Discard an enchantment, instant, or sorcery card.
|
||||
this.addAbility(new WardAbility(new DiscardTargetCost(new TargetCardInHand(filter)), false));
|
||||
|
||||
// Whenever you cast your second spell each turn, each opponent mills two cards. When one or more cards are milled this way, exile target enchantment, instant, or sorcery card with equal or lesser mana value than that spell from an opponent's graveyard. Copy the exiled card. You may cast the copy without paying its mana cost.
|
||||
this.addAbility(new CastSecondSpellTriggeredAbility(Zone.BATTLEFIELD, new SarumanOfManyColorsEffect(),
|
||||
TargetController.YOU, false, SetTargetPointer.SPELL));
|
||||
}
|
||||
|
||||
private SarumanOfManyColors(final SarumanOfManyColors card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SarumanOfManyColors copy() {
|
||||
return new SarumanOfManyColors(this);
|
||||
}
|
||||
}
|
||||
|
||||
class SarumanOfManyColorsEffect extends OneShotEffect {
|
||||
|
||||
public SarumanOfManyColorsEffect() {
|
||||
super(Outcome.PlayForFree);
|
||||
this.staticText = "each opponent mills two cards. When one or more cards are milled this way, exile target enchantment, instant, or sorcery card with equal or lesser mana value than that spell from an opponent's graveyard. Copy the exiled card. You may cast the copy without paying its mana cost.";
|
||||
}
|
||||
|
||||
public SarumanOfManyColorsEffect(final SarumanOfManyColorsEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// Keep track of whether or not the reflexive trigger needs to happen
|
||||
boolean trigger_second_part = false;
|
||||
|
||||
// Each opponent mills two cards
|
||||
for (UUID playerId : game.getOpponents(source.getControllerId())) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
// Only do the reflexive trigger if one or more cards were actually milled
|
||||
trigger_second_part |= !player.millCards(2, source, game).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
if (!trigger_second_part) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The second spell cast is just referenced, not targetted, so we might need to get LKI if the spell doesn't exist anymore (such as if it were countered)
|
||||
Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source));
|
||||
|
||||
// Create a filter for "enchantment, instant, or sorcery card with equal or lesser mana value than that spell"
|
||||
FilterCard filter = new FilterCard(
|
||||
"enchantment, instant, or sorcery card with equal or lesser mana value than that spell from an opponent's graveyard");
|
||||
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, spell.getManaValue() + 1));
|
||||
filter.add(SarumanOfManyColors.predicate);
|
||||
|
||||
ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
|
||||
new ExileTargetCardCopyAndCastEffect(true), false,
|
||||
"When one or more cards are milled this way, exile target enchantment, instant, or sorcery card with equal or lesser mana value than that spell from an opponent's graveyard."
|
||||
+ "Copy the exiled card. You may cast the copy without paying its mana cost.");
|
||||
ability.addTarget(new TargetCardInOpponentsGraveyard(filter));
|
||||
|
||||
game.fireReflexiveTriggeredAbility(ability, source);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SarumanOfManyColorsEffect copy() {
|
||||
return new SarumanOfManyColorsEffect(this);
|
||||
}
|
||||
|
||||
}
|
|
@ -109,6 +109,7 @@ public final class TheLordOfTheRingsTalesOfMiddleEarth extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Rosie Cotton of South Lane", 27, Rarity.UNCOMMON, mage.cards.r.RosieCottonOfSouthLane.class));
|
||||
cards.add(new SetCardInfo("Samwise Gamgee", 222, Rarity.RARE, mage.cards.s.SamwiseGamgee.class));
|
||||
cards.add(new SetCardInfo("Samwise the Stouthearted", 28, Rarity.UNCOMMON, mage.cards.s.SamwiseTheStouthearted.class));
|
||||
cards.add(new SetCardInfo("Saruman of Many Colors", 223, Rarity.UNCOMMON, mage.cards.s.SarumanOfManyColors.class));
|
||||
cards.add(new SetCardInfo("Saruman the White", 67, Rarity.UNCOMMON, mage.cards.s.SarumanTheWhite.class));
|
||||
cards.add(new SetCardInfo("Saruman's Trickery", 68, Rarity.UNCOMMON, mage.cards.s.SarumansTrickery.class));
|
||||
cards.add(new SetCardInfo("Sauron, the Lidless Eye", 288, Rarity.MYTHIC, mage.cards.s.SauronTheLidlessEye.class));
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
package org.mage.test.cards.single.ltr;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
|
||||
public class SarumanOfManyColorsTest extends CardTestPlayerBase {
|
||||
|
||||
static final String saruman = "Saruman of Many Colors";
|
||||
|
||||
@Test
|
||||
// Author: alexander-novo
|
||||
// A simple test to make sure the ward ability is working correctly
|
||||
public void wardTest() {
|
||||
String bolt = "Lightning Bolt";
|
||||
addCard(Zone.BATTLEFIELD, playerB, saruman);
|
||||
// Two bolts for casting on Saruman, one for discarding
|
||||
addCard(Zone.HAND, playerA, bolt, 3);
|
||||
// A red herring card to ignore on the second cast
|
||||
addCard(Zone.HAND, playerA, "Mountain");
|
||||
|
||||
// Mana for casting 2 lightning bolts
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, saruman, true);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, saruman, true);
|
||||
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertHandCount(playerA, bolt, 0);
|
||||
assertGraveyardCount(playerA, bolt, 3);
|
||||
assertDamageReceived(playerB, saruman, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
// Author: alexander-novo
|
||||
// A test to make sure the happy path of casting a second spell works
|
||||
public void secondSpellTest() {
|
||||
String bolt = "Lightning Bolt";
|
||||
|
||||
// Two spells to cast to trigger
|
||||
addCard(Zone.HAND, playerA, saruman);
|
||||
addCard(Zone.HAND, playerA, bolt);
|
||||
addCard(Zone.LIBRARY, playerB, bolt);
|
||||
|
||||
// Mana for casting spells
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
|
||||
skipInitShuffling();
|
||||
|
||||
// Cast saruman, and then a second spell - make sure saruman triggers
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, saruman, true);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
|
||||
checkStackObject("Bolt Check", 1, PhaseStep.PRECOMBAT_MAIN, playerA,
|
||||
"Whenever you cast your second spell each turn", 1);
|
||||
|
||||
// Resolve the mill trigger - make sure the correct cards were milled and that the reflexive ability has triggered
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1);
|
||||
// I don't know why this is needed
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerB, true);
|
||||
checkGraveyardCount("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerB, bolt, 1);
|
||||
checkGraveyardCount("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Mountain", 1);
|
||||
checkStackObject("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerA,
|
||||
"When one or more cards are milled this way", 1);
|
||||
|
||||
// Resolve the reflexive triggered ability - exiling the milled lightning bolt and casting it
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1);
|
||||
|
||||
// Choose player B to target for the next lightning bolt. Check to make sure there are now two lightning bolts on the stack
|
||||
addTarget(playerA, playerB);
|
||||
checkStackObject("Final check", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + bolt, 2);
|
||||
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 2 * 3);
|
||||
assertGraveyardCount(playerB, bolt, 0);
|
||||
assertExileCount(playerB, bolt, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
// Author: alexander-novo
|
||||
// A test to make sure the mana value restriction works properly
|
||||
public void manaValueTest() {
|
||||
String bolt = "Lightning Bolt";
|
||||
String helix = "Lightning Helix";
|
||||
|
||||
// Two spells to cast to trigger
|
||||
addCard(Zone.HAND, playerA, saruman);
|
||||
addCard(Zone.HAND, playerA, bolt);
|
||||
addCard(Zone.LIBRARY, playerB, helix); // This time, put a card we can't cast
|
||||
|
||||
// Mana for casting spells
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
|
||||
skipInitShuffling();
|
||||
|
||||
// Cast saruman, and then a second spell - make sure saruman triggers
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, saruman, true);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
|
||||
checkStackObject("Bolt Check", 1, PhaseStep.PRECOMBAT_MAIN, playerA,
|
||||
"Whenever you cast your second spell each turn", 1);
|
||||
|
||||
// Resolve the mill trigger - make sure the correct cards were milled and that the reflexive ability hasn't triggered because there are no targets
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1);
|
||||
// I don't know why this is needed
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerB, true);
|
||||
checkGraveyardCount("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerB, helix, 1);
|
||||
checkGraveyardCount("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Mountain", 1);
|
||||
checkStackObject("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerA,
|
||||
"When one or more cards are milled this way", 0);
|
||||
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 3);
|
||||
assertGraveyardCount(playerB, helix, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
// Author: alexander-novo
|
||||
// A test to make sure the triggered ability still works if the original triggering spell is removed from the stack (such as by countering)
|
||||
public void counterSpellTest() {
|
||||
String bolt = "Lightning Bolt";
|
||||
String counter = "Counterspell";
|
||||
|
||||
// Two spells to cast to trigger. Counter to test LKI
|
||||
addCard(Zone.HAND, playerA, saruman);
|
||||
addCard(Zone.HAND, playerA, bolt);
|
||||
addCard(Zone.HAND, playerB, counter);
|
||||
addCard(Zone.LIBRARY, playerB, bolt);
|
||||
|
||||
// Mana for casting spells
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
|
||||
|
||||
skipInitShuffling();
|
||||
|
||||
// Cast saruman, and then a second spell - make sure saruman triggers
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, saruman, true);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
|
||||
checkStackObject("Bolt Check", 1, PhaseStep.PRECOMBAT_MAIN, playerA,
|
||||
"Whenever you cast your second spell each turn", 1);
|
||||
|
||||
// Counter the original lightning bolt cast. Everything else should work the same as if this hadn't happened
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, counter, bolt, bolt);
|
||||
|
||||
// Resolve the mill trigger - make sure the correct cards were milled and that the reflexive ability has triggered
|
||||
showStack("Counter check", 1, PhaseStep.PRECOMBAT_MAIN, playerB);
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 2);
|
||||
// I don't know why this is needed
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA, true);
|
||||
checkGraveyardCount("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerB, bolt, 1);
|
||||
checkGraveyardCount("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Mountain", 1);
|
||||
checkStackObject("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerA,
|
||||
"When one or more cards are milled this way", 1);
|
||||
|
||||
// Resolve the reflexive triggered ability - exiling the milled lightning bolt and casting it
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1);
|
||||
|
||||
// Choose player B to target for the next lightning bolt. Check to make sure there are now two lightning bolts on the stack
|
||||
addTarget(playerA, playerB);
|
||||
checkStackObject("Final check", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + bolt, 1);
|
||||
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
// Original lightning bolt was countered
|
||||
assertLife(playerB, 20 - 3);
|
||||
assertGraveyardCount(playerB, bolt, 0);
|
||||
assertExileCount(playerB, bolt, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
// Author: alexander-novo
|
||||
// A test to make sure the reflexive trigger doesn't happen if there were no cards milled
|
||||
public void noMillTest() {
|
||||
String bolt = "Lightning Bolt";
|
||||
|
||||
// Two spells to cast to trigger
|
||||
addCard(Zone.HAND, playerA, saruman);
|
||||
addCard(Zone.HAND, playerA, bolt);
|
||||
addCard(Zone.GRAVEYARD, playerB, bolt);
|
||||
|
||||
// Mana for casting spells
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Plains");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
|
||||
removeAllCardsFromLibrary(playerB);
|
||||
|
||||
// Cast saruman, and then a second spell - make sure saruman triggers
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, saruman, true);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bolt, playerB);
|
||||
checkStackObject("Bolt Check", 1, PhaseStep.PRECOMBAT_MAIN, playerA,
|
||||
"Whenever you cast your second spell each turn", 1);
|
||||
|
||||
// Resolve the mill trigger - make sure cards weren't milled, and the reflexive ability wasn't triggered because of it (even though there is a valid target in the lightning bolt)
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1);
|
||||
// I don't know why this is needed
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerB, true);
|
||||
checkGraveyardCount("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerB, bolt, 1);
|
||||
checkGraveyardCount("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Mountain", 0);
|
||||
checkStackObject("Mill check", 1, PhaseStep.PRECOMBAT_MAIN, playerA,
|
||||
"When one or more cards are milled this way", 0);
|
||||
|
||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 3);
|
||||
assertGraveyardCount(playerB, bolt, 1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
// Author: alexander-novo
|
||||
// An effect for cards which instruct you to exile a card, then copy that card, and cast it.
|
||||
// NOTE: You must set effect text on your own
|
||||
public class ExileTargetCardCopyAndCastEffect extends OneShotEffect {
|
||||
|
||||
private final boolean optional;
|
||||
private final boolean noMana;
|
||||
|
||||
public ExileTargetCardCopyAndCastEffect(boolean noMana) {
|
||||
this(noMana, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: You must supply your own effect text
|
||||
* @param noMana Whether the copy can be cast without paying its mana cost
|
||||
* @param optional Whether the casting of the copy is optional (otherwise it must be cast if possible)
|
||||
*/
|
||||
public ExileTargetCardCopyAndCastEffect(boolean noMana, boolean optional) {
|
||||
super(Outcome.PlayForFree);
|
||||
|
||||
this.optional = optional;
|
||||
this.noMana = noMana;
|
||||
}
|
||||
|
||||
public ExileTargetCardCopyAndCastEffect(final ExileTargetCardCopyAndCastEffect effect) {
|
||||
super(effect);
|
||||
|
||||
this.optional = effect.optional;
|
||||
this.noMana = effect.noMana;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (player == null || card == null) {
|
||||
return false;
|
||||
}
|
||||
player.moveCards(card, Zone.EXILED, source, game);
|
||||
Card cardCopy = game.copyCard(card, source, source.getControllerId());
|
||||
if (optional && !player.chooseUse(outcome, "Cast copy of " +
|
||||
card.getName() + " without paying its mana cost?", source, game)) {
|
||||
return true;
|
||||
}
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + cardCopy.getId(), Boolean.TRUE);
|
||||
player.cast(
|
||||
player.chooseAbilityForCast(cardCopy, game, this.noMana),
|
||||
game, this.noMana, new ApprovingObject(source, game));
|
||||
game.getState().setValue("PlayFromNotOwnHandZone" + cardCopy.getId(), null);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExileTargetCardCopyAndCastEffect copy() {
|
||||
return new ExileTargetCardCopyAndCastEffect(this);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue