From 00deb7ac8aee590e5a0b0641ad5fba9302dcd051 Mon Sep 17 00:00:00 2001 From: Matthew Wilson Date: Fri, 23 Feb 2024 05:18:16 +0200 Subject: [PATCH] [MKM] Implement Intrude on the Mind (#11825) --- .../src/mage/cards/i/IntrudeOnTheMind.java | 161 ++++++++++++++++++ .../src/mage/sets/MurdersAtKarlovManor.java | 1 + .../token/Thopter00ColorlessToken.java | 29 ++++ 3 files changed, 191 insertions(+) create mode 100644 Mage.Sets/src/mage/cards/i/IntrudeOnTheMind.java create mode 100644 Mage/src/main/java/mage/game/permanent/token/Thopter00ColorlessToken.java diff --git a/Mage.Sets/src/mage/cards/i/IntrudeOnTheMind.java b/Mage.Sets/src/mage/cards/i/IntrudeOnTheMind.java new file mode 100644 index 00000000000..bf55022d140 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/IntrudeOnTheMind.java @@ -0,0 +1,161 @@ +package mage.cards.i; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.counters.CounterType; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.token.Thopter00ColorlessToken; +import mage.game.permanent.token.Token; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetCard; +import mage.target.common.TargetOpponent; + +/** + * + * @author DominionSpy + */ +public final class IntrudeOnTheMind extends CardImpl { + + public IntrudeOnTheMind(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); + + // Reveal the top five cards of your library and separate them into two piles. An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard. + // Create a 0/0 colorless Thopter artifact creature token with flying, then put a +1/+1 counter on it for each card put into your graveyard this way. + this.getSpellAbility().addEffect(new IntrudeOnTheMindEffect()); + } + + private IntrudeOnTheMind(final IntrudeOnTheMind card) { + super(card); + } + + @Override + public IntrudeOnTheMind copy() { + return new IntrudeOnTheMind(this); + } +} + +class IntrudeOnTheMindEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard("cards to put in the first pile"); + + IntrudeOnTheMindEffect() { + super(Outcome.DrawCard); + staticText = "Reveal the top five cards of your library and separate them into two piles. " + + "An opponent chooses one of those piles. Put that pile into your hand and the other into your graveyard. " + + "Create a 0/0 colorless Thopter artifact creature token with flying, " + + "then put a +1/+1 counter on it for each card put into your graveyard this way."; + } + + private IntrudeOnTheMindEffect(final IntrudeOnTheMindEffect effect) { + super(effect); + } + + @Override + public IntrudeOnTheMindEffect copy() { + return new IntrudeOnTheMindEffect(this); + } + + /** + * Pile-choosing logic based on {@link mage.abilities.effects.common.RevealAndSeparatePilesEffect}. + */ + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + + Cards allCards = new CardsImpl(controller.getLibrary().getTopCards(game, 5)); + Cards cards = allCards.copy(); + controller.revealCards(source, cards, game); + + TargetCard target = new TargetCard(0, cards.size(), Zone.LIBRARY, filter); + List pile1 = new ArrayList<>(); + controller.choose(Outcome.Neutral, cards, target, source, game); + target.getTargets() + .stream() + .map(game::getCard) + .filter(Objects::nonNull) + .forEach(pile1::add); + cards.removeIf(target.getTargets()::contains); + List pile2 = new ArrayList<>(); + pile2.addAll(cards.getCards(game)); + + Player opponent = getOpponent(controller, game, source); + if (opponent == null) { + return false; + } + boolean choice = opponent.choosePile(outcome, "Choose a pile to put into hand.", pile1, pile2, game); + + Zone pile1Zone = choice ? Zone.HAND : Zone.GRAVEYARD; + Zone pile2Zone = choice ? Zone.GRAVEYARD : Zone.HAND; + + game.informPlayers("Pile 1, going to " + pile1Zone + ": " + (pile1.isEmpty() ? " (none)" : + pile1.stream().map(MageObject::getName).collect(Collectors.joining(", ")))); + cards.clear(); + cards.addAllCards(pile1); + controller.moveCards(cards, pile1Zone, source, game); + + game.informPlayers("Pile 2, going to " + pile2Zone + ": " + (pile2.isEmpty() ? " (none)" : + pile2.stream().map(MageObject::getName).collect(Collectors.joining(", ")))); + cards.clear(); + cards.addAllCards(pile2); + controller.moveCards(cards, pile2Zone, source, game); + + Token token = new Thopter00ColorlessToken(); + token.putOntoBattlefield(1, game, source); + + allCards.retainZone(Zone.GRAVEYARD, game); + int count = allCards.size(); + if (count <= 0) { + return true; + } + for (UUID tokenId : token.getLastAddedTokenIds()) { + Permanent permanent = game.getPermanent(tokenId); + if (permanent == null) { + continue; + } + permanent.addCounters(CounterType.P1P1.createInstance(count), source.getControllerId(), source, game); + } + return true; + } + + private static Player getOpponent(Player controller, Game game, Ability source) { + Player opponent; + Set opponents = game.getOpponents(source.getControllerId()); + if (opponents.isEmpty()) { + return null; + } + if (opponents.size() == 1) { + opponent = game.getPlayer(opponents.iterator().next()); + } else { + Target targetOpponent = new TargetOpponent(true); + controller.chooseTarget(Outcome.Neutral, targetOpponent, source, game); + opponent = game.getPlayer(targetOpponent.getFirstTarget()); + if (opponent == null) { + return null; + } + game.informPlayers(controller.getLogName() + " chose " + opponent.getLogName() + " to choose the piles"); + } + return opponent; + } +} diff --git a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java index f10f806a1b1..bad62a8b1ab 100644 --- a/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java +++ b/Mage.Sets/src/mage/sets/MurdersAtKarlovManor.java @@ -134,6 +134,7 @@ public final class MurdersAtKarlovManor extends ExpansionSet { cards.add(new SetCardInfo("Innocent Bystander", 133, Rarity.COMMON, mage.cards.i.InnocentBystander.class)); cards.add(new SetCardInfo("Inside Source", 19, Rarity.COMMON, mage.cards.i.InsideSource.class)); cards.add(new SetCardInfo("Insidious Roots", 208, Rarity.UNCOMMON, mage.cards.i.InsidiousRoots.class)); + cards.add(new SetCardInfo("Intrude on the Mind", 61, Rarity.MYTHIC, mage.cards.i.IntrudeOnTheMind.class)); cards.add(new SetCardInfo("Island", 273, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("It Doesn't Add Up", 89, Rarity.UNCOMMON, mage.cards.i.ItDoesntAddUp.class)); cards.add(new SetCardInfo("Izoni, Center of the Web", 209, Rarity.RARE, mage.cards.i.IzoniCenterOfTheWeb.class)); diff --git a/Mage/src/main/java/mage/game/permanent/token/Thopter00ColorlessToken.java b/Mage/src/main/java/mage/game/permanent/token/Thopter00ColorlessToken.java new file mode 100644 index 00000000000..dd5d8a3a99a --- /dev/null +++ b/Mage/src/main/java/mage/game/permanent/token/Thopter00ColorlessToken.java @@ -0,0 +1,29 @@ +package mage.game.permanent.token; + +import mage.MageInt; +import mage.abilities.keyword.FlyingAbility; +import mage.constants.CardType; +import mage.constants.SubType; + +public class Thopter00ColorlessToken extends TokenImpl { + + public Thopter00ColorlessToken() { + super("Thopter Token", "0/0 colorless Thopter artifact creature token with flying"); + cardType.add(CardType.ARTIFACT); + cardType.add(CardType.CREATURE); + subtype.add(SubType.THOPTER); + power = new MageInt(0); + toughness = new MageInt(0); + + addAbility(FlyingAbility.getInstance()); + } + + private Thopter00ColorlessToken(final Thopter00ColorlessToken token) { + super(token); + } + + @Override + public Thopter00ColorlessToken copy() { + return new Thopter00ColorlessToken(this); + } +}