From c408776ad74609cbdbc48a9c313a7f47d9314dc2 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Thu, 15 Apr 2021 09:21:01 -0400 Subject: [PATCH] [STX] Implemented Mavinda, Students' Advocate --- .../mage/cards/m/MavindaStudentsAdvocate.java | 138 ++++++++++++++++++ .../mage/sets/StrixhavenSchoolOfMages.java | 1 + ...astCardFromGraveyardThenExileItEffect.java | 32 ++-- .../main/java/mage/filter/StaticFilters.java | 6 + 4 files changed, 161 insertions(+), 16 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/m/MavindaStudentsAdvocate.java diff --git a/Mage.Sets/src/mage/cards/m/MavindaStudentsAdvocate.java b/Mage.Sets/src/mage/cards/m/MavindaStudentsAdvocate.java new file mode 100644 index 00000000000..680565811b8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/m/MavindaStudentsAdvocate.java @@ -0,0 +1,138 @@ +package mage.cards.m; + +import mage.MageInt; +import mage.MageObject; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.common.LimitedTimesPerTurnActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.CastCardFromGraveyardThenExileItEffect; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.*; +import mage.filter.StaticFilters; +import mage.game.Controllable; +import mage.game.Game; +import mage.target.Target; +import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; + +import java.util.Collection; +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class MavindaStudentsAdvocate extends CardImpl { + + public MavindaStudentsAdvocate(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}"); + + this.addSuperType(SuperType.LEGENDARY); + this.subtype.add(SubType.BIRD); + this.subtype.add(SubType.ADVISOR); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // {0}: You may cast target instant or sorcery card from your graveyard this turn. If that spell doesn't target a creature you control, it costs {8} more to cast this way. If that spell would be put into your graveyard, exile it instead. Activate only once each turn. + Ability ability = new LimitedTimesPerTurnActivatedAbility( + Zone.BATTLEFIELD, new MavindaStudentsAdvocateEffect(), new GenericManaCost(0) + ); + ability.addTarget(new TargetCardInYourGraveyard( + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD + )); + this.addAbility(ability); + } + + private MavindaStudentsAdvocate(final MavindaStudentsAdvocate card) { + super(card); + } + + @Override + public MavindaStudentsAdvocate copy() { + return new MavindaStudentsAdvocate(this); + } +} + +class MavindaStudentsAdvocateEffect extends CastCardFromGraveyardThenExileItEffect { + + MavindaStudentsAdvocateEffect() { + super(); + staticText = "you may cast target instant or sorcery card from your graveyard this turn. " + + "If that spell doesn't target a creature you control, it costs {8} more to cast this way. " + + "If that spell would be put into your graveyard, exile it instead"; + } + + private MavindaStudentsAdvocateEffect(final MavindaStudentsAdvocateEffect effect) { + super(effect); + } + + @Override + public MavindaStudentsAdvocateEffect copy() { + return new MavindaStudentsAdvocateEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!super.apply(game, source)) { + return false; + } + Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); + if (card == null) { + return false; + } + game.addEffect(new MavindaStudentsAdvocateCostEffect(card, game), source); + return true; + } +} + +class MavindaStudentsAdvocateCostEffect extends CostModificationEffectImpl { + + private final MageObjectReference mor; + + MavindaStudentsAdvocateCostEffect(Card card, Game game) { + super(Duration.EndOfTurn, Outcome.Benefit, CostModificationType.INCREASE_COST); + mor = new MageObjectReference(card, game, 1); + } + + private MavindaStudentsAdvocateCostEffect(MavindaStudentsAdvocateCostEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + CardUtil.increaseCost(abilityToModify, 8); + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + return abilityToModify instanceof SpellAbility + && abilityToModify.isControlledBy(source.getControllerId()) + && mor.refersTo(((SpellAbility) abilityToModify).getCharacteristics(game), game) + && abilityToModify + .getTargets() + .stream() + .map(Target::getTargets) + .flatMap(Collection::stream) + .map(game::getPermanent) + .filter(Objects::nonNull) + .filter(MageObject::isCreature) + .map(Controllable::getControllerId) + .noneMatch(source::isControlledBy); + } + + @Override + public MavindaStudentsAdvocateCostEffect copy() { + return new MavindaStudentsAdvocateCostEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java index 6181224098c..d8e38f0e270 100644 --- a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java +++ b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java @@ -178,6 +178,7 @@ public final class StrixhavenSchoolOfMages extends ExpansionSet { cards.add(new SetCardInfo("Mascot Exhibition", 5, Rarity.MYTHIC, mage.cards.m.MascotExhibition.class)); cards.add(new SetCardInfo("Mascot Interception", 110, Rarity.UNCOMMON, mage.cards.m.MascotInterception.class)); cards.add(new SetCardInfo("Master Symmetrist", 138, Rarity.UNCOMMON, mage.cards.m.MasterSymmetrist.class)); + cards.add(new SetCardInfo("Mavinda, Students' Advocate", 21, Rarity.MYTHIC, mage.cards.m.MavindaStudentsAdvocate.class)); cards.add(new SetCardInfo("Mentor's Guidance", 46, Rarity.UNCOMMON, mage.cards.m.MentorsGuidance.class)); cards.add(new SetCardInfo("Mercurial Transformation", 47, Rarity.UNCOMMON, mage.cards.m.MercurialTransformation.class)); cards.add(new SetCardInfo("Mila, Crafty Companion", 153, Rarity.MYTHIC, mage.cards.m.MilaCraftyCompanion.class)); diff --git a/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java b/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java index cf38222a361..bebbe3b8650 100644 --- a/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/CastCardFromGraveyardThenExileItEffect.java @@ -20,7 +20,7 @@ public class CastCardFromGraveyardThenExileItEffect extends OneShotEffect { super(Outcome.Benefit); } - CastCardFromGraveyardThenExileItEffect(final CastCardFromGraveyardThenExileItEffect effect) { + protected CastCardFromGraveyardThenExileItEffect(final CastCardFromGraveyardThenExileItEffect effect) { super(effect); } @@ -32,15 +32,15 @@ public class CastCardFromGraveyardThenExileItEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Card card = game.getCard(this.getTargetPointer().getFirst(game, source)); - if (card != null) { - ContinuousEffect effect = new CastCardFromGraveyardEffect(); - effect.setTargetPointer(new FixedTarget(card, game)); - game.addEffect(effect, source); - effect = new ExileReplacementEffect(card.getId()); - game.addEffect(effect, source); - return true; + if (card == null) { + return false; } - return false; + ContinuousEffect effect = new CastCardFromGraveyardEffect(); + effect.setTargetPointer(new FixedTarget(card, game)); + game.addEffect(effect, source); + effect = new ExileReplacementEffect(card.getId()); + game.addEffect(effect, source); + return true; } } @@ -51,7 +51,7 @@ class CastCardFromGraveyardEffect extends AsThoughEffectImpl { this.staticText = "You may cast target card from your graveyard"; } - CastCardFromGraveyardEffect(final CastCardFromGraveyardEffect effect) { + private CastCardFromGraveyardEffect(final CastCardFromGraveyardEffect effect) { super(effect); } @@ -67,7 +67,8 @@ class CastCardFromGraveyardEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { - return objectId.equals(this.getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId()); + return objectId.equals(this.getTargetPointer().getFirst(game, source)) + && affectedControllerId.equals(source.getControllerId()); } } @@ -81,7 +82,7 @@ class ExileReplacementEffect extends ReplacementEffectImpl { this.staticText = "If that card would be put into your graveyard this turn, exile it instead"; } - ExileReplacementEffect(final ExileReplacementEffect effect) { + private ExileReplacementEffect(final ExileReplacementEffect effect) { super(effect); this.cardId = effect.cardId; } @@ -95,10 +96,9 @@ class ExileReplacementEffect extends ReplacementEffectImpl { public boolean replaceEvent(GameEvent event, Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); Card card = game.getCard(this.cardId); - if (controller != null && card != null) { - return controller.moveCards(card, Zone.EXILED, source, game); - } - return false; + return controller != null + && card != null + && controller.moveCards(card, Zone.EXILED, source, game); } @Override diff --git a/Mage/src/main/java/mage/filter/StaticFilters.java b/Mage/src/main/java/mage/filter/StaticFilters.java index f57414b75a9..146451c1aae 100644 --- a/Mage/src/main/java/mage/filter/StaticFilters.java +++ b/Mage/src/main/java/mage/filter/StaticFilters.java @@ -114,6 +114,12 @@ public final class StaticFilters { FILTER_CARD_FROM_YOUR_GRAVEYARD.setLockedFilter(true); } + public static final FilterCard FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD = new FilterInstantOrSorceryCard("instant or sorcery card from your graveyard"); + + static { + FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD.setLockedFilter(true); + } + public static final FilterNoncreatureCard FILTER_CARD_NON_CREATURE = new FilterNoncreatureCard(); static {