From f97b6bf53807e738c437a33419be853caab3b257 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sun, 11 Apr 2021 10:28:39 -0400 Subject: [PATCH] [STX] Implemented Wandering Archaic / Explore the Vastlands --- .../src/mage/cards/w/WanderingArchaic.java | 195 ++++++++++++++++++ .../mage/sets/StrixhavenSchoolOfMages.java | 1 + .../SpellCastOpponentTriggeredAbility.java | 1 + 3 files changed, 197 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/w/WanderingArchaic.java diff --git a/Mage.Sets/src/mage/cards/w/WanderingArchaic.java b/Mage.Sets/src/mage/cards/w/WanderingArchaic.java new file mode 100644 index 0000000000..e090f1fc94 --- /dev/null +++ b/Mage.Sets/src/mage/cards/w/WanderingArchaic.java @@ -0,0 +1,195 @@ +package mage.cards.w; + +import mage.abilities.Ability; +import mage.abilities.common.SpellCastOpponentTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; +import mage.game.Game; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class WanderingArchaic extends ModalDoubleFacesCard { + + public WanderingArchaic(UUID ownerId, CardSetInfo setInfo) { + super( + ownerId, setInfo, + new CardType[]{CardType.CREATURE}, new SubType[]{SubType.AVATAR}, "{5}", + "Explore the Vastlands", + new CardType[]{CardType.SORCERY}, new SubType[]{}, "{3}" + ); + + // 1. + // Wandering Archaic + // Creature - Avatar + this.getLeftHalfCard().setPT(4, 4); + + // Whenever an opponent casts an instant or sorcery spell, they may pay {2}. If they don't, you may copy that spell. You may choose new targets for the copy. + this.getLeftHalfCard().addAbility(new SpellCastOpponentTriggeredAbility( + Zone.BATTLEFIELD, new WanderingArchaicEffect(), + StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, + false, SetTargetPointer.PLAYER + )); + + // 2. + // Explore the Vastlands + // Sorcery + // Each player looks at the top five cards of their library, reveals a land card and/or an instant or sorcery card from among them, then puts the cards they revealed this way into their hand and the rest on the bottom of their library in a random order. Each player gains 3 life. + this.getSpellAbility().addEffect(new ExploreTheVastlandsEffect()); + } + + private WanderingArchaic(final WanderingArchaic card) { + super(card); + } + + @Override + public WanderingArchaic copy() { + return new WanderingArchaic(this); + } +} + +class WanderingArchaicEffect extends OneShotEffect { + + WanderingArchaicEffect() { + super(Outcome.Benefit); + staticText = "they may pay {2}. If they don't, you may copy that spell. You may choose new targets for the copy"; + } + + private WanderingArchaicEffect(final WanderingArchaicEffect effect) { + super(effect); + } + + @Override + public WanderingArchaicEffect copy() { + return new WanderingArchaicEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); + Spell spell = (Spell) getValue("spellCast"); + if (controller == null || opponent == null || spell == null) { + return false; + } + Cost cost = new GenericManaCost(2); + if (cost.canPay( + source, source, opponent.getId(), game + ) && opponent.chooseUse( + outcome, "Pay {2} to prevent " + controller.getName() + + " from copying " + spell.getName() + "?", source, game + ) && cost.pay( + source, game, source, opponent.getId(), false + ) && controller.chooseUse( + outcome, "Copy " + spell.getName() + "?", source, game + )) { + spell.createCopyOnStack(game, source, controller.getId(), true); + } + return true; + } +} + +class ExploreTheVastlandsEffect extends OneShotEffect { + + ExploreTheVastlandsEffect() { + super(Outcome.Benefit); + staticText = "each player looks at the top five cards of their library, " + + "reveals a land card and/or an instant or sorcery card from among them, " + + "then puts the cards they revealed this way into their hand and the rest " + + "on the bottom of their library in a random order. Each player gains 3 life"; + } + + private ExploreTheVastlandsEffect(final ExploreTheVastlandsEffect effect) { + super(effect); + } + + @Override + public ExploreTheVastlandsEffect copy() { + return new ExploreTheVastlandsEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player == null) { + continue; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 5)); + TargetCard target = new ExploreTheVastlandsTarget(); + player.choose(outcome, cards, target, game); + Cards toHand = new CardsImpl(target.getTargets()); + cards.removeIf(target.getTargets()::contains); + player.revealCards(source, toHand, game); + player.moveCards(toHand, Zone.HAND, source, game); + player.putCardsOnBottomOfLibrary(cards, game, source, false); + } + for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + player.gainLife(3, game, source); + } + } + return true; + } +} + +class ExploreTheVastlandsTarget extends TargetCardInLibrary { + + private static final FilterCard filter + = new FilterCard("a land card and/or an instant or sorcery card"); + + static { + filter.add(Predicates.or( + CardType.LAND.getPredicate(), + CardType.SORCERY.getPredicate(), + CardType.INSTANT.getPredicate() + )); + } + + ExploreTheVastlandsTarget() { + super(0, 2, filter); + } + + private ExploreTheVastlandsTarget(final ExploreTheVastlandsTarget target) { + super(target); + } + + @Override + public ExploreTheVastlandsTarget copy() { + return new ExploreTheVastlandsTarget(this); + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + if (!super.canTarget(playerId, id, source, game)) { + return false; + } + Card card = game.getCard(id); + if (card == null) { + return false; + } + if (this.getTargets().isEmpty()) { + return true; + } + boolean isLand = card.isLand(); + return this.getTargets() + .stream() + .map(game::getCard) + .filter(Objects::nonNull) + .noneMatch(c -> card.isLand() && c.isLand() || card.isInstantOrSorcery() && c.isInstantOrSorcery()); + } +} diff --git a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java index 22cf6340b7..d7abd92588 100644 --- a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java +++ b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java @@ -284,6 +284,7 @@ public final class StrixhavenSchoolOfMages extends ExpansionSet { cards.add(new SetCardInfo("Venerable Warsinger", 246, Rarity.RARE, mage.cards.v.VenerableWarsinger.class)); cards.add(new SetCardInfo("Vineglimmer Snarl", 274, Rarity.RARE, mage.cards.v.VineglimmerSnarl.class)); cards.add(new SetCardInfo("Vortex Runner", 60, Rarity.COMMON, mage.cards.v.VortexRunner.class)); + cards.add(new SetCardInfo("Wandering Archaic", 6, Rarity.RARE, mage.cards.w.WanderingArchaic.class)); cards.add(new SetCardInfo("Waterfall Aerialist", 61, Rarity.COMMON, mage.cards.w.WaterfallAerialist.class)); cards.add(new SetCardInfo("Witherbloom Apprentice", 247, Rarity.UNCOMMON, mage.cards.w.WitherbloomApprentice.class)); cards.add(new SetCardInfo("Witherbloom Campus", 275, Rarity.COMMON, mage.cards.w.WitherbloomCampus.class)); diff --git a/Mage/src/main/java/mage/abilities/common/SpellCastOpponentTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/SpellCastOpponentTriggeredAbility.java index 47ced845e9..f31025f50b 100644 --- a/Mage/src/main/java/mage/abilities/common/SpellCastOpponentTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/SpellCastOpponentTriggeredAbility.java @@ -64,6 +64,7 @@ public class SpellCastOpponentTriggeredAbility extends TriggeredAbilityImpl { if (spell == null || !filter.match(spell, getSourceId(), getControllerId(), game)) { return false; } + getEffects().setValue("spellCast", spell); switch (setTargetPointer) { case NONE: break;