-
Notifications
You must be signed in to change notification settings - Fork 787
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MKM] Implement Intrude on the Mind #11825
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
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.ThopterColorlessToken2; | ||
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<Card> 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<Card> pile2 = new ArrayList<>(); | ||
pile2.addAll(cards.getCards(game)); | ||
|
||
Player opponent = getOpponent(controller, game, source); | ||
boolean choice = opponent.choosePile(outcome, "Choose a pile to put into hand.", pile1, pile2, game); | ||
xenohedron marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
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 ThopterColorlessToken2(); | ||
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<UUID> 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) { | ||
game.informPlayers(controller.getLogName() + " chose " + opponent.getLogName() + " to choose the piles"); | ||
} else { | ||
game.informPlayers(controller.getLogName() + " chose nothing" + " to choose the piles"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (idk why bother with info message on failed choice, shouldn't happen in normal usage) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah you still need to null check it though |
||
} | ||
} | ||
return opponent; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ThopterColorlessToken2 extends TokenImpl { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer something more descriptive than tacking on a number, maybe "Thopter00ColorlessToken" ? Not critical |
||
|
||
public ThopterColorlessToken2() { | ||
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 ThopterColorlessToken2(final ThopterColorlessToken2 token) { | ||
super(token); | ||
} | ||
|
||
@Override | ||
public ThopterColorlessToken2 copy() { | ||
return new ThopterColorlessToken2(this); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow cards these days have so much text. It's unfortunate this has to duplicate logic from the existing class but given the constraints this custom class does seem to be the way to go.