mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
implement [MKM] Deadly Cover-Up; refactor to common class (#12500)
* Genericize most variants of SearchTargetGraveyardHandLibraryForCardNameAndExileEffect * Implement Deadly Cover-Up * int maxAmount instead of boolean maxFour, use player.chooseTarget * Fix Surgical Extraction test, use withNotTarget for exile choices * Add tests, fix MDFC's back sides' names from being matched against
This commit is contained in:
parent
0a23521ece
commit
f53954d56a
21 changed files with 349 additions and 662 deletions
|
|
@ -1,13 +1,14 @@
|
|||
|
||||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.effects.common.CounterTargetAndSearchGraveyardHandLibraryEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
|
@ -21,7 +22,7 @@ public final class Counterbore extends CardImpl {
|
|||
// Counter target spell.
|
||||
// Search its controller's graveyard, hand, and library for all cards with the same name as that spell and exile them. Then that player shuffles their library.
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
this.getSpellAbility().addEffect(new CounterTargetAndSearchGraveyardHandLibraryEffect().concatBy("."));
|
||||
this.getSpellAbility().addEffect(new CounterTargetAndSearchGraveyardHandLibraryEffect());
|
||||
}
|
||||
|
||||
private Counterbore(final Counterbore card) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.effects.common.ExileTargetAndSearchGraveyardHandLibraryEffect;
|
||||
import mage.abilities.keyword.DevoidAbility;
|
||||
import mage.cards.CardImpl;
|
||||
|
|
@ -9,6 +8,8 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.target.common.TargetNonBasicLandPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
|
|
@ -23,7 +24,7 @@ public final class CrumbleToDust extends CardImpl {
|
|||
|
||||
// Exile target nonbasic land. Search its controller's graveyard, hand, and library for any number of cards with the same name as that land and exile them. Then that player shuffles their library.
|
||||
this.getSpellAbility().addTarget(new TargetNonBasicLandPermanent());
|
||||
this.getSpellAbility().addEffect(new ExileTargetAndSearchGraveyardHandLibraryEffect(false, "its controller's", "any number of cards with the same name as that land"));
|
||||
this.getSpellAbility().addEffect(new ExileTargetAndSearchGraveyardHandLibraryEffect(true, "its controller's", "any number of cards with the same name as that land"));
|
||||
}
|
||||
|
||||
private CrumbleToDust(final CrumbleToDust card) {
|
||||
|
|
|
|||
85
Mage.Sets/src/mage/cards/d/DeadlyCoverUp.java
Normal file
85
Mage.Sets/src/mage/cards/d/DeadlyCoverUp.java
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.common.CollectedEvidenceCondition;
|
||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||
import mage.abilities.effects.common.DestroyAllEffect;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
import mage.abilities.keyword.CollectEvidenceAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInOpponentsGraveyard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author notgreat
|
||||
*/
|
||||
public final class DeadlyCoverUp extends CardImpl {
|
||||
|
||||
public DeadlyCoverUp(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{B}{B}");
|
||||
|
||||
|
||||
// As an additional cost to cast this spell, you may collect evidence 6.
|
||||
this.addAbility(new CollectEvidenceAbility(8));
|
||||
|
||||
// Destroy all creatures. If evidence was collected, exile a card from an opponent's graveyard. Then search its owner's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles, then draws a card for each card exiled from their hand this way.
|
||||
this.getSpellAbility().addEffect(new DestroyAllEffect(StaticFilters.FILTER_PERMANENT_CREATURES));
|
||||
this.getSpellAbility().addEffect(new ConditionalOneShotEffect(new DeadlyCoverUpEffect(), CollectedEvidenceCondition.instance,
|
||||
"If evidence was collected, exile a card from an opponent's graveyard. Then search its owner's "
|
||||
+ "graveyard, hand, and library for any number of cards with that name and exile them. "
|
||||
+ "That player shuffles, then draws a card for each card exiled from their hand this way."));
|
||||
}
|
||||
|
||||
private DeadlyCoverUp(final DeadlyCoverUp card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeadlyCoverUp copy() {
|
||||
return new DeadlyCoverUp(this);
|
||||
}
|
||||
}
|
||||
|
||||
class DeadlyCoverUpEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
|
||||
|
||||
DeadlyCoverUpEffect() {
|
||||
super(true, "its owner's", "any number of cards with the same name as that card", true);
|
||||
this.staticText = "exile a card from an opponent's graveyard. Then search its owner's "
|
||||
+ "graveyard, hand, and library for any number of cards with that name and exile them. "
|
||||
+ "That player shuffles, then draws a card for each card exiled from their hand this way.";
|
||||
}
|
||||
|
||||
private DeadlyCoverUpEffect(final DeadlyCoverUpEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeadlyCoverUpEffect copy() {
|
||||
return new DeadlyCoverUpEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
TargetCardInOpponentsGraveyard target = new TargetCardInOpponentsGraveyard(StaticFilters.FILTER_CARD);
|
||||
target.withNotTarget(true);
|
||||
if (player != null && player.chooseTarget(Outcome.Exile, target, source, game)) {
|
||||
Card cardToExile = game.getCard(target.getFirstTarget());
|
||||
if (cardToExile == null) {
|
||||
return false;
|
||||
}
|
||||
player.moveCards(cardToExile, Zone.EXILED, source, game);
|
||||
this.applySearchAndExile(game, source, cardToExile.getName(), cardToExile.getOwnerId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,19 @@
|
|||
package mage.cards.e;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
import mage.abilities.keyword.SplitSecondAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInGraveyard;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -57,10 +49,10 @@ public final class Extirpate extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class ExtirpateEffect extends OneShotEffect {
|
||||
class ExtirpateEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
|
||||
|
||||
ExtirpateEffect() {
|
||||
super(Outcome.Exile);
|
||||
super(false, "its owner's", "all cards with the same name as that card");
|
||||
this.staticText = "Choose target card in a graveyard other than "
|
||||
+ "a basic land card. Search its owner's graveyard, hand, "
|
||||
+ "and library for all cards with the same name "
|
||||
|
|
@ -78,60 +70,10 @@ class ExtirpateEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = game.getObject(source);
|
||||
Card chosenCard = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (chosenCard != null && sourceObject != null && controller != null) {
|
||||
Player owner = game.getPlayer(chosenCard.getOwnerId());
|
||||
if (owner == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Exile all cards with the same name
|
||||
// Building a card filter with the name
|
||||
FilterCard filterNamedCard = new FilterCard();
|
||||
String nameToSearch = CardUtil.getCardNameForSameNameSearch(chosenCard);
|
||||
filterNamedCard.add(new NamePredicate(nameToSearch));
|
||||
|
||||
// The cards you're searching for must be found and exiled if they're in the graveyard because it's a public zone.
|
||||
// Finding those cards in the hand and library is optional, because those zones are hidden (even if the hand is temporarily revealed).
|
||||
// search cards in graveyard
|
||||
for (Card checkCard : owner.getGraveyard().getCards(game)) {
|
||||
if (checkCard.getName().equals(chosenCard.getName())) {
|
||||
controller.moveCardToExileWithInfo(checkCard, null, "", source, game, Zone.GRAVEYARD, true);
|
||||
}
|
||||
}
|
||||
|
||||
// search cards in hand
|
||||
filterNamedCard.setMessage("card named " + chosenCard.getLogName() + " in the hand of " + owner.getLogName());
|
||||
TargetCard targetCardInHand = new TargetCard(0, Integer.MAX_VALUE, Zone.HAND, filterNamedCard);
|
||||
targetCardInHand.withNotTarget(true);
|
||||
if (controller.chooseTarget(Outcome.Exile, owner.getHand(), targetCardInHand, source, game)) {
|
||||
List<UUID> targets = targetCardInHand.getTargets();
|
||||
for (UUID targetId : targets) {
|
||||
Card targetCard = owner.getHand().get(targetId, game);
|
||||
if (targetCard != null) {
|
||||
controller.moveCardToExileWithInfo(targetCard, null, "", source, game, Zone.HAND, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search cards in Library
|
||||
filterNamedCard.setMessage("card named " + chosenCard.getName() + " in the library of " + owner.getName());
|
||||
TargetCardInLibrary targetCardInLibrary = new TargetCardInLibrary(0, Integer.MAX_VALUE, filterNamedCard);
|
||||
if (controller.searchLibrary(targetCardInLibrary, source, game, owner.getId())) {
|
||||
List<UUID> targets = targetCardInLibrary.getTargets();
|
||||
for (UUID targetId : targets) {
|
||||
Card targetCard = owner.getLibrary().getCard(targetId, game);
|
||||
if (targetCard != null) {
|
||||
controller.moveCardToExileWithInfo(targetCard, null, "", source, game, Zone.LIBRARY, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
owner.shuffleLibrary(source, game);
|
||||
return true;
|
||||
if (chosenCard != null) {
|
||||
return super.applySearchAndExile(game, source, CardUtil.getCardNameForSameNameSearch(chosenCard), chosenCard.getOwnerId());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,20 +2,20 @@ package mage.cards.l;
|
|||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.*;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.TargetPlayer;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -43,7 +43,7 @@ public final class Lobotomy extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class LobotomyEffect extends OneShotEffect {
|
||||
class LobotomyEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("card other than a basic land card");
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ class LobotomyEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
public LobotomyEffect() {
|
||||
super(Outcome.Benefit);
|
||||
super(false, "that player's", "all cards with the same name as the chosen card");
|
||||
staticText = "Target player reveals their hand, then you choose a card other than a basic land card from it. Search that player's graveyard, hand, and library for all cards with the same name as the chosen card and exile them. Then that player shuffles";
|
||||
}
|
||||
|
||||
|
|
@ -78,54 +78,7 @@ class LobotomyEffect extends OneShotEffect {
|
|||
chosenCard = game.getCard(target.getFirstTarget());
|
||||
}
|
||||
|
||||
// Exile all cards with the same name
|
||||
// Building a card filter with the name
|
||||
FilterCard filterNamedCards = new FilterCard();
|
||||
String nameToSearch = "---";// so no card matches
|
||||
if (chosenCard != null) {
|
||||
nameToSearch = CardUtil.getCardNameForSameNameSearch(chosenCard);
|
||||
filterNamedCards.setMessage("cards named " + nameToSearch);
|
||||
}
|
||||
filterNamedCards.add(new NamePredicate(nameToSearch));
|
||||
Cards cardsToExile = new CardsImpl();
|
||||
// The cards you're searching for must be found and exiled if they're in the graveyard because it's a public zone.
|
||||
// Finding those cards in the hand and library is optional, because those zones are hidden (even if the hand is temporarily revealed).
|
||||
// search cards in graveyard
|
||||
if (chosenCard != null) {
|
||||
for (Card checkCard : targetPlayer.getGraveyard().getCards(game)) {
|
||||
if (checkCard.getName().equals(chosenCard.getName())) {
|
||||
cardsToExile.add(checkCard);
|
||||
}
|
||||
}
|
||||
// search cards in hand
|
||||
TargetCard targetCardsHand = new TargetCard(0, Integer.MAX_VALUE, Zone.HAND, filterNamedCards);
|
||||
controller.chooseTarget(outcome, targetPlayer.getHand(), targetCardsHand, source, game);
|
||||
for (UUID cardId : targetCardsHand.getTargets()) {
|
||||
Card card = game.getCard(cardId);
|
||||
if (card != null) {
|
||||
cardsToExile.add(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search cards in Library
|
||||
// If the player has no nonland cards in their hand, you can still search that player's library and have that player shuffle it.
|
||||
if (chosenCard != null || controller.chooseUse(outcome, "Search library anyway?", source, game)) {
|
||||
TargetCardInLibrary targetCardsLibrary = new TargetCardInLibrary(0, Integer.MAX_VALUE, filterNamedCards);
|
||||
controller.searchLibrary(targetCardsLibrary, source, game, targetPlayer.getId());
|
||||
for (UUID cardId : targetCardsLibrary.getTargets()) {
|
||||
Card card = game.getCard(cardId);
|
||||
if (card != null) {
|
||||
cardsToExile.add(card);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!cardsToExile.isEmpty()) {
|
||||
controller.moveCards(cardsToExile, Zone.EXILED, source, game);
|
||||
}
|
||||
targetPlayer.shuffleLibrary(source, game);
|
||||
return true;
|
||||
return applySearchAndExile(game, source, CardUtil.getCardNameForSameNameSearch(chosenCard), getTargetPointer().getFirst(game, source));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,18 @@
|
|||
|
||||
package mage.cards.l;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.common.ChooseACardNameEffect;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
|
@ -43,8 +41,7 @@ public final class LostLegacy extends CardImpl {
|
|||
class LostLegacyEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
|
||||
|
||||
LostLegacyEffect() {
|
||||
super(true, "target player's", "any number of cards with that name");
|
||||
this.staticText = "Search target player's graveyard, hand, and library for any number of cards with that name and exile them. That player shuffles, then draws a card for each card exiled from their hand this way";
|
||||
super(true, "target player's", "any number of cards with that name", true);
|
||||
}
|
||||
|
||||
private LostLegacyEffect(final LostLegacyEffect effect) {
|
||||
|
|
@ -56,15 +53,7 @@ class LostLegacyEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExi
|
|||
String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
|
||||
Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (targetPlayer != null && cardName != null && !cardName.isEmpty()) {
|
||||
FilterCard filter = new FilterCard();
|
||||
filter.add(new NamePredicate(cardName));
|
||||
int cardsInHandBefore = targetPlayer.getHand().count(filter, game);
|
||||
boolean result = super.applySearchAndExile(game, source, cardName, getTargetPointer().getFirst(game, source));
|
||||
int cardsExiled = cardsInHandBefore - targetPlayer.getHand().count(filter, game);
|
||||
if (cardsExiled > 0) {
|
||||
targetPlayer.drawCards(cardsExiled, source, game);
|
||||
}
|
||||
return result;
|
||||
return applySearchAndExile(game, source, cardName, getTargetPointer().getFirst(game, source));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ public final class Necromentia extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
//Based on SearchTargetGraveyardHandLibraryForCardNameAndExileEffect
|
||||
class NecromentiaEffect extends OneShotEffect {
|
||||
|
||||
NecromentiaEffect() {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ import mage.MageInt;
|
|||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
|
@ -16,11 +15,9 @@ import mage.constants.Zone;
|
|||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterNonlandCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -57,7 +54,7 @@ public final class ShimianSpecter extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class ShimianSpecterEffect extends OneShotEffect {
|
||||
class ShimianSpecterEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("nonland card");
|
||||
|
||||
|
|
@ -66,7 +63,7 @@ class ShimianSpecterEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
public ShimianSpecterEffect() {
|
||||
super(Outcome.Benefit);
|
||||
super(false, "that player's", "all cards with the same name as that card");
|
||||
staticText = "that player reveals their hand. You choose a nonland card from it. "
|
||||
+ "Search that player's graveyard, hand, and library for all cards "
|
||||
+ "with the same name as that card and exile them. Then that "
|
||||
|
|
@ -92,62 +89,10 @@ class ShimianSpecterEffect extends OneShotEffect {
|
|||
// You choose a nonland card from it
|
||||
TargetCard target = new TargetCard(Zone.HAND, new FilterNonlandCard());
|
||||
target.withNotTarget(true);
|
||||
Card chosenCard = null;
|
||||
if (target.canChoose(controller.getId(), source, game)
|
||||
&& controller.chooseTarget(Outcome.Benefit, targetPlayer.getHand(), target, source, game)) {
|
||||
chosenCard = game.getCard(target.getFirstTarget());
|
||||
return applySearchAndExile(game, source, CardUtil.getCardNameForSameNameSearch(game.getCard(target.getFirstTarget())), getTargetPointer().getFirst(game, source));
|
||||
}
|
||||
|
||||
// Exile all cards with the same name
|
||||
// Building a card filter with the name
|
||||
FilterCard filterNamedCards = new FilterCard();
|
||||
String nameToSearch = "---";// so no card matches
|
||||
if (chosenCard != null) {
|
||||
nameToSearch = CardUtil.getCardNameForSameNameSearch(chosenCard);
|
||||
}
|
||||
filterNamedCards.add(new NamePredicate(nameToSearch));
|
||||
|
||||
// The cards you're searching for must be found and exiled if they're
|
||||
// in the graveyard because it's a public zone.
|
||||
// Finding those cards in the hand and library is optional, because
|
||||
// those zones are hidden (even if the hand is temporarily revealed).
|
||||
// search cards in graveyard
|
||||
if (chosenCard != null) {
|
||||
for (Card checkCard : targetPlayer.getGraveyard().getCards(game)) {
|
||||
if (checkCard.getName().equals(chosenCard.getName())) {
|
||||
controller.moveCardToExileWithInfo(checkCard, null, "",
|
||||
source, game, Zone.GRAVEYARD, true);
|
||||
}
|
||||
}
|
||||
|
||||
// search cards in hand
|
||||
TargetCard targetHandCards = new TargetCard(0, Integer.MAX_VALUE, Zone.HAND, filterNamedCards);
|
||||
controller.chooseTarget(Outcome.Benefit, targetPlayer.getHand(), targetHandCards, source, game);
|
||||
for (UUID cardId : targetHandCards.getTargets()) {
|
||||
Card card = game.getCard(cardId);
|
||||
if (card != null) {
|
||||
controller.moveCardToExileWithInfo(card, null, "",
|
||||
source, game, Zone.HAND, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search cards in Library
|
||||
// If the player has no nonland cards in their hand, you can still search
|
||||
// that player's library and have that player shuffle it.
|
||||
if (chosenCard != null
|
||||
|| controller.chooseUse(outcome, "Search library anyway?", source, game)) {
|
||||
TargetCardInLibrary targetCardsLibrary = new TargetCardInLibrary(0, Integer.MAX_VALUE, filterNamedCards);
|
||||
controller.searchLibrary(targetCardsLibrary, source, game, targetPlayer.getId());
|
||||
for (UUID cardId : targetCardsLibrary.getTargets()) {
|
||||
Card card = game.getCard(cardId);
|
||||
if (card != null) {
|
||||
controller.moveCardToExileWithInfo(card, null, "", source, game, Zone.LIBRARY, true);
|
||||
}
|
||||
}
|
||||
targetPlayer.shuffleLibrary(source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,18 @@
|
|||
package mage.cards.s;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInGraveyard;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -53,10 +46,10 @@ public final class SurgicalExtraction extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class SurgicalExtractionEffect extends OneShotEffect {
|
||||
class SurgicalExtractionEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
|
||||
|
||||
SurgicalExtractionEffect() {
|
||||
super(Outcome.Detriment);
|
||||
super(true, "its owner's", "any number of cards with the same name as that card");
|
||||
this.staticText = "Choose target card in a graveyard other than a basic land card. "
|
||||
+ "Search its owner's graveyard, hand, and library for any number of cards "
|
||||
+ "with the same name as that card and exile them. Then that player shuffles";
|
||||
|
|
@ -73,68 +66,10 @@ class SurgicalExtractionEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
// 6/1/2011 "Any number of cards" means just that. If you wish, you can choose to
|
||||
// leave some or all of the cards with the same name as the targeted card,
|
||||
// including that card, in the zone they're in.
|
||||
|
||||
Card chosenCard = game.getCard(source.getFirstTarget());
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (chosenCard != null && controller != null) {
|
||||
Player owner = game.getPlayer(chosenCard.getOwnerId());
|
||||
if (owner != null) {
|
||||
String nameToSearch = CardUtil.getCardNameForSameNameSearch(chosenCard);
|
||||
FilterCard filterNamedCard = new FilterCard("card named " + nameToSearch);
|
||||
filterNamedCard.add(new NamePredicate(nameToSearch));
|
||||
|
||||
// cards in Graveyard
|
||||
int cardsCount = owner.getGraveyard().count(filterNamedCard, game);
|
||||
if (cardsCount > 0) {
|
||||
filterNamedCard.setMessage("card named " + nameToSearch
|
||||
+ " in the graveyard of " + owner.getName());
|
||||
TargetCardInGraveyard target = new TargetCardInGraveyard(0, cardsCount, filterNamedCard);
|
||||
if (controller.chooseTarget(Outcome.Exile, owner.getGraveyard(), target, source, game)) {
|
||||
List<UUID> targets = target.getTargets();
|
||||
for (UUID targetId : targets) {
|
||||
Card targetCard = owner.getGraveyard().get(targetId, game);
|
||||
if (targetCard != null) {
|
||||
controller.moveCardToExileWithInfo(targetCard, null,
|
||||
"", source, game, Zone.GRAVEYARD, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cards in Hand
|
||||
filterNamedCard.setMessage("card named " + nameToSearch + " in the hand of " + owner.getName());
|
||||
TargetCard targetCardInHand = new TargetCard(0, Integer.MAX_VALUE, Zone.HAND, filterNamedCard);
|
||||
targetCardInHand.withNotTarget(true);
|
||||
if (controller.chooseTarget(Outcome.Exile, owner.getHand(), targetCardInHand, source, game)) {
|
||||
List<UUID> targets = targetCardInHand.getTargets();
|
||||
for (UUID targetId : targets) {
|
||||
Card targetCard = owner.getHand().get(targetId, game);
|
||||
if (targetCard != null) {
|
||||
controller.moveCardToExileWithInfo(targetCard, null, "", source, game, Zone.HAND, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cards in Library
|
||||
filterNamedCard.setMessage("card named " + nameToSearch + " in the library of " + owner.getName());
|
||||
TargetCardInLibrary targetCardInLibrary = new TargetCardInLibrary(0, Integer.MAX_VALUE, filterNamedCard);
|
||||
if (controller.searchLibrary(targetCardInLibrary, source, game, owner.getId())) {
|
||||
List<UUID> targets = targetCardInLibrary.getTargets();
|
||||
for (UUID targetId : targets) {
|
||||
Card targetCard = owner.getLibrary().getCard(targetId, game);
|
||||
if (targetCard != null) {
|
||||
controller.moveCardToExileWithInfo(targetCard, null, "", source, game, Zone.LIBRARY, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
owner.shuffleLibrary(source, game);
|
||||
return true;
|
||||
}
|
||||
Card chosenCard = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (chosenCard != null) {
|
||||
return applySearchAndExile(game, source, CardUtil.getCardNameForSameNameSearch(chosenCard), chosenCard.getOwnerId());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,12 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.abilities.effects.common.CounterTargetAndSearchGraveyardHandLibraryEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetSpell;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -26,7 +18,7 @@ public final class TestOfTalents extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{U}");
|
||||
|
||||
// Counter target instant or sorcery spell. Search its controller's graveyard, hand, and library for any number of cards with the same name as that spell and exile them. That player shuffles, then draws a card for each card exiled from their hand this way.
|
||||
this.getSpellAbility().addEffect(new TestOfTalentsEffect());
|
||||
this.getSpellAbility().addEffect(new CounterTargetAndSearchGraveyardHandLibraryEffect(true, "its controller's", "any number of cards with the same name as that spell", true));
|
||||
this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_INSTANT_OR_SORCERY));
|
||||
}
|
||||
|
||||
|
|
@ -39,64 +31,3 @@ public final class TestOfTalents extends CardImpl {
|
|||
return new TestOfTalents(this);
|
||||
}
|
||||
}
|
||||
|
||||
class TestOfTalentsEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
|
||||
|
||||
TestOfTalentsEffect() {
|
||||
super(false, "its controller's", "all cards with the same name as that spell");
|
||||
staticText = "counter target instant or sorcery spell. Search its controller's graveyard, hand, " +
|
||||
"and library for any number of cards with the same name as that spell and exile them. " +
|
||||
"That player shuffles, then draws a card for each card exiled from their hand this way";
|
||||
}
|
||||
|
||||
private TestOfTalentsEffect(final TestOfTalentsEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestOfTalentsEffect copy() {
|
||||
return new TestOfTalentsEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget());
|
||||
if (stackObject == null) {
|
||||
return false;
|
||||
}
|
||||
MageObject targetObject = game.getObject(stackObject.getSourceId());
|
||||
String cardName;
|
||||
if (targetObject instanceof Card) {
|
||||
cardName = targetObject.getName();
|
||||
} else {
|
||||
cardName = "";
|
||||
}
|
||||
UUID searchPlayerId = stackObject.getControllerId();
|
||||
Player player = game.getPlayer(searchPlayerId);
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
int previousCount = player
|
||||
.getHand()
|
||||
.getCards(game)
|
||||
.stream()
|
||||
.map(MageObject::getName)
|
||||
.filter(Objects::nonNull)
|
||||
.mapToInt(s -> CardUtil.haveSameNames(s, cardName) ? 1 : 0)
|
||||
.sum();
|
||||
game.getStack().counter(source.getFirstTarget(), source, game);
|
||||
this.applySearchAndExile(game, source, cardName, searchPlayerId);
|
||||
int newCount = player
|
||||
.getHand()
|
||||
.getCards(game)
|
||||
.stream()
|
||||
.map(MageObject::getName)
|
||||
.filter(Objects::nonNull)
|
||||
.mapToInt(s -> CardUtil.haveSameNames(s, cardName) ? 1 : 0)
|
||||
.sum();
|
||||
if (previousCount > newCount) {
|
||||
player.drawCards(previousCount - newCount, source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,13 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.FatefulHourCondition;
|
||||
import mage.abilities.effects.common.ExileTargetAndSearchGraveyardHandLibraryEffect;
|
||||
import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCreatureOrPlaneswalker;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -35,7 +29,7 @@ public final class TheEnd extends CardImpl {
|
|||
).setRuleAtTheTop(true));
|
||||
|
||||
// Exile target creature or planeswalker. Search its controller's graveyard, hand, and library for any number of cards with the same name as that permanent and exile them. That player shuffles, then draws card for each card exiled from their hand this way.
|
||||
this.getSpellAbility().addEffect(new TheEndEffect());
|
||||
this.getSpellAbility().addEffect(new ExileTargetAndSearchGraveyardHandLibraryEffect(true, "its controller's", "any number of cards with the same name as that permanent"));
|
||||
this.getSpellAbility().addTarget(new TargetCreatureOrPlaneswalker());
|
||||
}
|
||||
|
||||
|
|
@ -48,48 +42,3 @@ public final class TheEnd extends CardImpl {
|
|||
return new TheEnd(this);
|
||||
}
|
||||
}
|
||||
|
||||
class TheEndEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
|
||||
|
||||
TheEndEffect() {
|
||||
super(true, "its controller's", "any number of cards with the same name as that permanent");
|
||||
this.staticText = "Exile target creature or planeswalker. Search its controller's graveyard, hand, and library" +
|
||||
" for any number of cards with the same name as that permanent and exile them." +
|
||||
" That player shuffles, then draws a card for each card exiled from their hand this way";
|
||||
}
|
||||
|
||||
private TheEndEffect(final TheEndEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheEndEffect copy() {
|
||||
return new TheEndEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getFirstTarget());
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
String name = permanent.getName();
|
||||
Player player = game.getPlayer(permanent.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
player.moveCards(permanent, Zone.EXILED, source, game);
|
||||
|
||||
FilterCard filter = new FilterCard();
|
||||
filter.add(new NamePredicate(name));
|
||||
|
||||
int cardsInHandBefore = player.getHand().count(filter, game);
|
||||
boolean result = super.applySearchAndExile(game, source, name, player.getId());
|
||||
int cardsExiled = cardsInHandBefore - player.getHand().count(filter, game);
|
||||
if (cardsExiled > 0) {
|
||||
player.drawCards(cardsExiled, source, game);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,22 +5,13 @@ import mage.abilities.common.ActivateAsSorceryActivatedAbility;
|
|||
import mage.abilities.costs.common.ExileSourceCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ChooseACardNameEffect;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
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.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.common.TargetOpponent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -56,12 +47,10 @@ public final class TheStoneBrain extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class TheStoneBrainEffect extends OneShotEffect {
|
||||
class TheStoneBrainEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
|
||||
|
||||
TheStoneBrainEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "Search target opponent's graveyard, hand, and library for up to four cards with that name " +
|
||||
"and exile them. That player shuffles, then draws a card for each card exiled from their hand this way";
|
||||
super(true, "target opponent's", "up to four cards with that name", true, 4);
|
||||
}
|
||||
|
||||
private TheStoneBrainEffect(final TheStoneBrainEffect effect) {
|
||||
|
|
@ -76,61 +65,6 @@ class TheStoneBrainEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (cardName != null && controller != null) {
|
||||
int numberOfCardsStillToRemove = 4;
|
||||
int numberOfCardsExiledFromHand = 0;
|
||||
Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (targetPlayer != null) {
|
||||
FilterCard filter = new FilterCard("card named " + cardName);
|
||||
filter.add(new NamePredicate(cardName));
|
||||
|
||||
// cards in Graveyard
|
||||
int cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getGraveyard().count(filter, game));
|
||||
if (cardsCount > 0) {
|
||||
filter.setMessage("card named " + cardName + " in the graveyard of " + targetPlayer.getName());
|
||||
TargetCard target = new TargetCard(Math.min(cardsCount, numberOfCardsStillToRemove),
|
||||
Math.min(cardsCount, numberOfCardsStillToRemove), Zone.GRAVEYARD, filter);
|
||||
if (controller.choose(Outcome.Exile, targetPlayer.getGraveyard(), target, source, game)) {
|
||||
numberOfCardsStillToRemove -= target.getTargets().size();
|
||||
controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);
|
||||
}
|
||||
}
|
||||
|
||||
// cards in Hand
|
||||
if (numberOfCardsStillToRemove > 0) {
|
||||
cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getHand().count(filter, game));
|
||||
filter.setMessage("card named " + cardName + " in the hand of " + targetPlayer.getName());
|
||||
TargetCard target = new TargetCard(0, Math.min(cardsCount, numberOfCardsStillToRemove), Zone.HAND, filter);
|
||||
if (controller.choose(Outcome.Exile, targetPlayer.getHand(), target, source, game)) {
|
||||
numberOfCardsExiledFromHand = target.getTargets().size();
|
||||
numberOfCardsStillToRemove -= target.getTargets().size();
|
||||
controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);
|
||||
}
|
||||
}
|
||||
|
||||
// cards in Library
|
||||
if (numberOfCardsStillToRemove > 0) {
|
||||
Cards cardsInLibrary = new CardsImpl();
|
||||
cardsInLibrary.addAllCards(targetPlayer.getLibrary().getCards(game));
|
||||
cardsCount = (cardName.isEmpty() ? 0 : cardsInLibrary.count(filter, game));
|
||||
filter.setMessage("card named " + cardName + " in the library of " + targetPlayer.getLogName());
|
||||
TargetCardInLibrary targetLib = new TargetCardInLibrary(0, Math.min(cardsCount, numberOfCardsStillToRemove), filter);
|
||||
if (controller.choose(Outcome.Exile, cardsInLibrary, targetLib, source, game)) {
|
||||
controller.moveCards(new CardsImpl(targetLib.getTargets()), Zone.EXILED, source, game);
|
||||
}
|
||||
}
|
||||
targetPlayer.shuffleLibrary(source, game);
|
||||
|
||||
if (numberOfCardsExiledFromHand > 0) {
|
||||
game.processAction();
|
||||
targetPlayer.drawCards(numberOfCardsExiledFromHand, source, game);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return applySearchAndExile(game, source, cardName, getTargetPointer().getFirst(game, source));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,26 +2,18 @@ package mage.cards.t;
|
|||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ChooseACardNameEffect;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetPlayer;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.util.CardUtil;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.target.TargetCard;
|
||||
|
||||
/**
|
||||
* @author jeffwadsworth
|
||||
|
|
@ -52,7 +44,7 @@ public final class ThoughtHemorrhage extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class ThoughtHemorrhageEffect extends OneShotEffect {
|
||||
class ThoughtHemorrhageEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
|
||||
|
||||
static final String rule = "Target player reveals their hand. "
|
||||
+ "{this} deals 3 damage to that player for each card with "
|
||||
|
|
@ -62,7 +54,7 @@ class ThoughtHemorrhageEffect extends OneShotEffect {
|
|||
+ "Then that player shuffles";
|
||||
|
||||
public ThoughtHemorrhageEffect() {
|
||||
super(Outcome.Exile);
|
||||
super(false, "that player's", "all cards with that name");
|
||||
staticText = rule;
|
||||
}
|
||||
|
||||
|
|
@ -93,49 +85,8 @@ class ThoughtHemorrhageEffect extends OneShotEffect {
|
|||
if (cardsFound > 0) {
|
||||
targetPlayer.damage(3 * cardsFound, source.getSourceId(), source, game);
|
||||
}
|
||||
// Exile all cards with the same name
|
||||
// Building a card filter with the name
|
||||
FilterCard filterNamedCards = new FilterCard();
|
||||
filterNamedCards.add(new NamePredicate(cardName));
|
||||
|
||||
Set<Card> toExile = new LinkedHashSet<>();
|
||||
// The cards you're searching for must be found and exiled if
|
||||
// they're in the graveyard because it's a public zone.
|
||||
// Finding those cards in the hand and library is optional,
|
||||
// because those zones are hidden (even if the hand is temporarily revealed).
|
||||
// search cards in graveyard
|
||||
for (Card checkCard : targetPlayer.getGraveyard().getCards(game)) {
|
||||
if (checkCard.getName().equals(cardName)) {
|
||||
toExile.add(checkCard);
|
||||
}
|
||||
}
|
||||
|
||||
// search cards in Hand
|
||||
TargetCard targetCardInHand = new TargetCard(0, Integer.MAX_VALUE, Zone.HAND, filterNamedCards);
|
||||
if (controller.chooseTarget(Outcome.Exile, targetPlayer.getHand(), targetCardInHand, source, game)) {
|
||||
List<UUID> targets = targetCardInHand.getTargets();
|
||||
for (UUID targetId : targets) {
|
||||
Card targetCard = targetPlayer.getHand().get(targetId, game);
|
||||
if (targetCard != null) {
|
||||
toExile.add(targetCard);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// search cards in Library
|
||||
// If the player has no nonland cards in their hand, you can still search
|
||||
// that player's library and have that player shuffle it.
|
||||
TargetCardInLibrary targetCardsLibrary = new TargetCardInLibrary(0, Integer.MAX_VALUE, filterNamedCards);
|
||||
controller.searchLibrary(targetCardsLibrary, source, game, targetPlayer.getId());
|
||||
for (UUID cardId : targetCardsLibrary.getTargets()) {
|
||||
Card card = game.getCard(cardId);
|
||||
if (card != null) {
|
||||
toExile.add(card);
|
||||
}
|
||||
}
|
||||
controller.moveCards(toExile, Zone.EXILED, source, game);
|
||||
targetPlayer.shuffleLibrary(source, game);
|
||||
return true;
|
||||
return applySearchAndExile(game, source, cardName, source.getFirstTarget());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -1,24 +1,16 @@
|
|||
package mage.cards.u;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ChooseACardNameEffect;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
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.filter.FilterCard;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.common.TargetOpponent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
|
@ -44,11 +36,10 @@ public final class UnmooredEgo extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class UnmooredEgoEffect extends OneShotEffect {
|
||||
class UnmooredEgoEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
|
||||
|
||||
UnmooredEgoEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "Search target opponent's graveyard, hand, and library for up to four cards with that name and exile them. That player shuffles, then draws a card for each card exiled from their hand this way";
|
||||
super(false, "target opponent's", "up to four cards with that name", true, 4);
|
||||
}
|
||||
|
||||
private UnmooredEgoEffect(final UnmooredEgoEffect effect) {
|
||||
|
|
@ -63,62 +54,6 @@ class UnmooredEgoEffect extends OneShotEffect {
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
String cardName = (String) game.getState().getValue(source.getSourceId().toString() + ChooseACardNameEffect.INFO_KEY);
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (cardName != null && controller != null) {
|
||||
int numberOfCardsStillToRemove = 4;
|
||||
int numberOfCardsExiledFromHand = 0;
|
||||
Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
|
||||
if (targetPlayer != null) {
|
||||
FilterCard filter = new FilterCard("card named " + cardName);
|
||||
filter.add(new NamePredicate(cardName));
|
||||
|
||||
// cards in Graveyard
|
||||
int cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getGraveyard().count(filter, game));
|
||||
if (cardsCount > 0) {
|
||||
filter.setMessage("card named " + cardName + " in the graveyard of " + targetPlayer.getName());
|
||||
TargetCard target = new TargetCard(Math.min(cardsCount, numberOfCardsStillToRemove),
|
||||
Math.min(cardsCount, numberOfCardsStillToRemove), Zone.GRAVEYARD, filter);
|
||||
if (controller.choose(Outcome.Exile, targetPlayer.getGraveyard(), target, source, game)) {
|
||||
numberOfCardsStillToRemove -= target.getTargets().size();
|
||||
controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);
|
||||
}
|
||||
}
|
||||
|
||||
// cards in Hand
|
||||
if (numberOfCardsStillToRemove > 0) {
|
||||
cardsCount = (cardName.isEmpty() ? 0 : targetPlayer.getHand().count(filter, game));
|
||||
filter.setMessage("card named " + cardName + " in the hand of " + targetPlayer.getName());
|
||||
TargetCard target = new TargetCard(0, Math.min(cardsCount, numberOfCardsStillToRemove), Zone.HAND, filter);
|
||||
if (controller.choose(Outcome.Exile, targetPlayer.getHand(), target, source, game)) {
|
||||
numberOfCardsExiledFromHand = target.getTargets().size();
|
||||
numberOfCardsStillToRemove -= target.getTargets().size();
|
||||
controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);
|
||||
}
|
||||
}
|
||||
|
||||
// cards in Library
|
||||
if (numberOfCardsStillToRemove > 0) {
|
||||
Cards cardsInLibrary = new CardsImpl();
|
||||
cardsInLibrary.addAllCards(targetPlayer.getLibrary().getCards(game));
|
||||
cardsCount = (cardName.isEmpty() ? 0 : cardsInLibrary.count(filter, game));
|
||||
filter.setMessage("card named " + cardName + " in the library of " + targetPlayer.getLogName());
|
||||
TargetCardInLibrary targetLib = new TargetCardInLibrary(0, Math.min(cardsCount, numberOfCardsStillToRemove), filter);
|
||||
if (controller.choose(Outcome.Exile, cardsInLibrary, targetLib, source, game)) {
|
||||
controller.moveCards(new CardsImpl(targetLib.getTargets()), Zone.EXILED, source, game);
|
||||
}
|
||||
}
|
||||
targetPlayer.shuffleLibrary(source, game);
|
||||
|
||||
if (numberOfCardsExiledFromHand > 0) {
|
||||
game.processAction();
|
||||
targetPlayer.drawCards(numberOfCardsExiledFromHand, source, game);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return applySearchAndExile(game, source, cardName, getTargetPointer().getFirst(game, source));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ public final class MurdersAtKarlovManor extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Curious Cadaver", 194, Rarity.UNCOMMON, mage.cards.c.CuriousCadaver.class));
|
||||
cards.add(new SetCardInfo("Curious Inquiry", 51, Rarity.UNCOMMON, mage.cards.c.CuriousInquiry.class));
|
||||
cards.add(new SetCardInfo("Deadly Complication", 195, Rarity.UNCOMMON, mage.cards.d.DeadlyComplication.class));
|
||||
cards.add(new SetCardInfo("Deadly Cover-Up", 83, Rarity.RARE, mage.cards.d.DeadlyCoverUp.class));
|
||||
cards.add(new SetCardInfo("Deduce", 52, Rarity.COMMON, mage.cards.d.Deduce.class));
|
||||
cards.add(new SetCardInfo("Defenestrated Phantom", 11, Rarity.COMMON, mage.cards.d.DefenestratedPhantom.class));
|
||||
cards.add(new SetCardInfo("Delney, Streetwise Lookout", 12, Rarity.MYTHIC, mage.cards.d.DelneyStreetwiseLookout.class));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
package org.mage.test.cards.abilities.oneshot.exile;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* {@link mage.cards.s.SurgicalExtraction Surgical Extraction}
|
||||
* {B/P}
|
||||
* Instant
|
||||
* ({B/P} can be paid with either {B} or 2 life.)
|
||||
* Choose target card in a graveyard other than a basic land card.
|
||||
* Search its owner’s graveyard, hand, and library for any number of cards with the same name as that card and exile them.
|
||||
* Then that player shuffles.
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SearchNameExileTests extends CardTestPlayerBase {
|
||||
/**
|
||||
* I noticed that surgical extraction did not allow me to select any cards
|
||||
* to exile when I targeted breaking // entering. It did however allow my
|
||||
* opponent to target lingering souls so it could be a split card
|
||||
* interaction or just a random glitch.
|
||||
*/
|
||||
@Test
|
||||
public void testSearchAndExileSplitCards() {
|
||||
addCard(Zone.HAND, playerA, "Surgical Extraction", 1); // Instant {B/P}
|
||||
|
||||
addCard(Zone.GRAVEYARD, playerB, "Breaking // Entering", 2);
|
||||
addCard(Zone.HAND, playerB, "Breaking // Entering", 1);
|
||||
addCard(Zone.LIBRARY, playerB, "Breaking // Entering", 1);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Surgical Extraction", "Breaking // Entering");
|
||||
setChoice(playerA, "Yes"); // Pay 2 life to cast instead of {B}
|
||||
setChoice(playerA, "Breaking // Entering^Breaking // Entering"); // Graveyard
|
||||
setChoice(playerA, "Breaking // Entering"); // Hand
|
||||
setChoice(playerA, "Breaking // Entering"); // Library
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Surgical Extraction", 1);
|
||||
|
||||
assertGraveyardCount(playerB, "Breaking // Entering", 0);
|
||||
assertLibraryCount(playerB, "Breaking // Entering", 0);
|
||||
assertHandCount(playerB, "Breaking // Entering", 0);
|
||||
assertHandCount(playerB, 0);
|
||||
|
||||
assertExileCount(playerB, "Breaking // Entering", 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link mage.cards.t.TestOfTalents Test Of Talents} 1U Instant
|
||||
* Counter target instant or sorcery spell. Search its controller’s graveyard, hand, and library for
|
||||
* any number of cards with the same name as that spell and exile them. That player shuffles,
|
||||
* then draws a card for each card exiled from their hand this way.
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void testSearchAndExileSplitSpell() {
|
||||
addCard(Zone.HAND, playerA, "Test of Talents", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
|
||||
addCard(Zone.GRAVEYARD, playerB, "Ready // Willing", 1);
|
||||
addCard(Zone.HAND, playerB, "Ready // Willing", 2);
|
||||
addCard(Zone.LIBRARY, playerB, "Ready // Willing", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Forest", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "fused Ready // Willing");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Test of Talents", "Ready // Willing", "Ready // Willing");
|
||||
setChoice(playerA, "Ready // Willing^Ready // Willing"); // Should be 2 in Graveyard now, take both
|
||||
setChoice(playerA, "Ready // Willing"); // Hand
|
||||
setChoice(playerA, "Ready // Willing"); // Library
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Test of Talents", 1);
|
||||
|
||||
assertGraveyardCount(playerB, "Ready // Willing", 0);
|
||||
assertLibraryCount(playerB, "Ready // Willing", 0);
|
||||
assertHandCount(playerB, "Ready // Willing", 0);
|
||||
assertHandCount(playerB, 1); //add 2, cast 1, last is exiled+redrawn
|
||||
|
||||
assertExileCount(playerB, "Ready // Willing", 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFailSearchAndExileMDFCSpell() {
|
||||
addCard(Zone.HAND, playerA, "Test of Talents", 1); // Instant 1U
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
|
||||
addCard(Zone.GRAVEYARD, playerB, "Flamescroll Celebrant", 1);
|
||||
addCard(Zone.HAND, playerB, "Flamescroll Celebrant", 2);
|
||||
addCard(Zone.LIBRARY, playerB, "Flamescroll Celebrant", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
|
||||
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Revel in Silence");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Test of Talents", "Revel in Silence", "Revel in Silence");
|
||||
|
||||
// Should be no choices in the graveyard available since the back side spell doesn't match the front side name
|
||||
// Non-strict mode tries to select all possible targets, no current method to check if choosing is impossible
|
||||
// setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
|
||||
assertGraveyardCount(playerB, "Flamescroll Celebrant", 2);
|
||||
assertLibraryCount(playerB, "Flamescroll Celebrant", 1);
|
||||
assertHandCount(playerB, "Flamescroll Celebrant", 1);
|
||||
assertHandCount(playerB, 1); //add 2, cast 1
|
||||
|
||||
assertExileCount(playerB, "Flamescroll Celebrant", 0);
|
||||
}
|
||||
|
||||
//Asserts "exile all possible" behavior for MDFC test above
|
||||
@Test
|
||||
public void testSearchAndExileSplitSpellNonstrict() {
|
||||
addCard(Zone.HAND, playerA, "Test of Talents", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
|
||||
|
||||
addCard(Zone.GRAVEYARD, playerB, "Ready // Willing", 1);
|
||||
addCard(Zone.HAND, playerB, "Ready // Willing", 2);
|
||||
addCard(Zone.LIBRARY, playerB, "Ready // Willing", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Plains", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Forest", 2);
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2);
|
||||
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "fused Ready // Willing");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Test of Talents", "Ready // Willing", "Ready // Willing");
|
||||
|
||||
// setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Test of Talents", 1);
|
||||
|
||||
assertGraveyardCount(playerB, "Ready // Willing", 0);
|
||||
assertLibraryCount(playerB, "Ready // Willing", 0);
|
||||
assertHandCount(playerB, "Ready // Willing", 0);
|
||||
assertHandCount(playerB, 1); //add 2, cast 1, last is exiled+redrawn
|
||||
|
||||
assertExileCount(playerB, "Ready // Willing", 4);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
package org.mage.test.cards.abilities.oneshot.exile;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* {@link mage.cards.s.SurgicalExtraction Surgical Extraction}
|
||||
* {B/P}
|
||||
* Instant
|
||||
* ({B/P} can be paid with either {B} or 2 life.)
|
||||
* Choose target card in a graveyard other than a basic land card.
|
||||
* Search its owner’s graveyard, hand, and library for any number of cards with the same name as that card and exile them.
|
||||
* Then that player shuffles.
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SurgicalExtractionTest extends CardTestPlayerBase {
|
||||
/**
|
||||
* I noticed that surgical extraction did not allow me to select any cards
|
||||
* to exile when I targeted breaking // entering. It did however allow my
|
||||
* opponent to target lingering souls so it could be a split card
|
||||
* interaction or just a random glitch.
|
||||
*/
|
||||
@Test
|
||||
public void testSearchAndExileSplitCards() {
|
||||
addCard(Zone.HAND, playerA, "Surgical Extraction", 1); // Instant {B/P}
|
||||
|
||||
addCard(Zone.GRAVEYARD, playerB, "Breaking // Entering", 2);
|
||||
addCard(Zone.HAND, playerB, "Breaking // Entering", 1);
|
||||
addCard(Zone.LIBRARY, playerB, "Breaking // Entering", 1);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Surgical Extraction", "Breaking // Entering");
|
||||
setChoice(playerA, "Yes"); // Pay 2 life to cast instead of {B}
|
||||
addTarget(playerA, "Breaking // Entering^Breaking // Entering"); // Graveyard
|
||||
addTarget(playerA, "Breaking // Entering"); // Hand
|
||||
addTarget(playerA, "Breaking // Entering"); // Library
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Surgical Extraction", 1);
|
||||
|
||||
assertGraveyardCount(playerB, "Breaking // Entering", 0);
|
||||
assertLibraryCount(playerB, "Breaking // Entering", 0);
|
||||
assertHandCount(playerB, "Breaking // Entering", 0);
|
||||
|
||||
assertExileCount(playerB, "Breaking // Entering", 4);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,15 @@
|
|||
package mage.abilities.effects.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.target.TargetSpell;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
|
@ -20,11 +19,15 @@ public class CounterTargetAndSearchGraveyardHandLibraryEffect extends SearchTarg
|
|||
|
||||
|
||||
public CounterTargetAndSearchGraveyardHandLibraryEffect() {
|
||||
this(false,"its controller's", "all cards with the same name as that spell" );
|
||||
this(false, "its controller's", "all cards with the same name as that spell");
|
||||
}
|
||||
|
||||
public CounterTargetAndSearchGraveyardHandLibraryEffect(boolean graveyardExileOptional, String searchWhatText, String searchForText) {
|
||||
super(graveyardExileOptional, searchWhatText, searchForText);
|
||||
this(graveyardExileOptional, searchWhatText, searchForText, false);
|
||||
}
|
||||
|
||||
public CounterTargetAndSearchGraveyardHandLibraryEffect(boolean graveyardExileOptional, String searchWhatText, String searchForText, boolean drawForEachHandCard) {
|
||||
super(graveyardExileOptional, searchWhatText, searchForText, drawForEachHandCard);
|
||||
}
|
||||
|
||||
protected CounterTargetAndSearchGraveyardHandLibraryEffect(final CounterTargetAndSearchGraveyardHandLibraryEffect effect) {
|
||||
|
|
@ -39,26 +42,19 @@ public class CounterTargetAndSearchGraveyardHandLibraryEffect extends SearchTarg
|
|||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
boolean result = false;
|
||||
|
||||
String cardName = "";
|
||||
UUID searchPlayerId = null;
|
||||
|
||||
if (source.getTargets().get(0) instanceof TargetSpell) {
|
||||
UUID objectId = source.getFirstTarget();
|
||||
StackObject stackObject = game.getStack().getStackObject(objectId);
|
||||
if (stackObject != null) {
|
||||
MageObject targetObject = game.getObject(stackObject.getSourceId());
|
||||
if (targetObject instanceof Card) {
|
||||
cardName = targetObject.getName();
|
||||
}
|
||||
searchPlayerId = stackObject.getControllerId();
|
||||
String cardName = stackObject.getName();
|
||||
UUID searchPlayerId = stackObject.getControllerId();
|
||||
result = game.getStack().counter(objectId, source, game);
|
||||
// 5/1/2008: If the targeted spell can't be countered (it's Vexing Shusher, for example),
|
||||
// that spell will remain on the stack. Counterbore will continue to resolve. You still
|
||||
// get to search for and exile all other cards with that name.
|
||||
this.applySearchAndExile(game, source, cardName, searchPlayerId);
|
||||
}
|
||||
}
|
||||
// 5/1/2008: If the targeted spell can't be countered (it's Vexing Shusher, for example),
|
||||
// that spell will remain on the stack. Counterbore will continue to resolve. You still
|
||||
// get to search for and exile all other cards with that name.
|
||||
this.applySearchAndExile(game, source, cardName, searchPlayerId);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,11 @@ public class ExileTargetAndSearchGraveyardHandLibraryEffect extends SearchTarget
|
|||
this.staticText = ""; // since parent class overrides static text but we need to use a target
|
||||
}
|
||||
|
||||
public ExileTargetAndSearchGraveyardHandLibraryEffect(boolean graveyardExileOptional, String searchWhatText, String searchForText, boolean drawForEachHandCard) {
|
||||
super(graveyardExileOptional, searchWhatText, searchForText, drawForEachHandCard);
|
||||
this.staticText = ""; // since parent class overrides static text but we need to use a target
|
||||
}
|
||||
|
||||
private ExileTargetAndSearchGraveyardHandLibraryEffect(final ExileTargetAndSearchGraveyardHandLibraryEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,13 +30,25 @@ public abstract class SearchTargetGraveyardHandLibraryForCardNameAndExileEffect
|
|||
* 2/1/2005: The copies must be found if they are in publicly viewable zones. Finding copies while searching private zones is optional.
|
||||
*/
|
||||
protected boolean graveyardExileOptional;
|
||||
protected boolean drawForEachHandCard;
|
||||
protected int maxAmount;
|
||||
|
||||
protected SearchTargetGraveyardHandLibraryForCardNameAndExileEffect(boolean graveyardExileOptional, String searchWhatText, String searchForText) {
|
||||
this(graveyardExileOptional, searchWhatText, searchForText, false);
|
||||
}
|
||||
|
||||
protected SearchTargetGraveyardHandLibraryForCardNameAndExileEffect(boolean graveyardExileOptional, String searchWhatText, String searchForText, boolean drawForEachHandCard) {
|
||||
this(graveyardExileOptional, searchWhatText, searchForText, drawForEachHandCard, Integer.MAX_VALUE);
|
||||
}
|
||||
protected SearchTargetGraveyardHandLibraryForCardNameAndExileEffect(boolean graveyardExileOptional, String searchWhatText, String searchForText, boolean drawForEachHandCard, int maxAmount) {
|
||||
super(Outcome.Exile);
|
||||
this.searchWhatText = searchWhatText;
|
||||
this.searchForText = searchForText;
|
||||
this.graveyardExileOptional = graveyardExileOptional;
|
||||
this.staticText = "search " + searchWhatText + " graveyard, hand, and library for " + searchForText + " and exile them. Then that player shuffles";
|
||||
this.drawForEachHandCard = drawForEachHandCard;
|
||||
this.maxAmount = maxAmount;
|
||||
this.staticText = "search " + searchWhatText + " graveyard, hand, and library for " + searchForText + " and exile them. " +
|
||||
(drawForEachHandCard ? "That player shuffles, then draws a card for each card exiled from their hand this way" : "Then that player shuffles");
|
||||
}
|
||||
|
||||
protected SearchTargetGraveyardHandLibraryForCardNameAndExileEffect(final SearchTargetGraveyardHandLibraryForCardNameAndExileEffect effect) {
|
||||
|
|
@ -44,6 +56,8 @@ public abstract class SearchTargetGraveyardHandLibraryForCardNameAndExileEffect
|
|||
this.searchWhatText = effect.searchWhatText;
|
||||
this.searchForText = effect.searchForText;
|
||||
this.graveyardExileOptional = effect.graveyardExileOptional;
|
||||
this.drawForEachHandCard = effect.drawForEachHandCard;
|
||||
this.maxAmount = effect.maxAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -59,37 +73,50 @@ public abstract class SearchTargetGraveyardHandLibraryForCardNameAndExileEffect
|
|||
if (cardName != null && controller != null) {
|
||||
Player targetPlayer = game.getPlayer(targetPlayerId);
|
||||
if (targetPlayer != null) {
|
||||
int handCards = 0;
|
||||
int maxRemaining = maxAmount;
|
||||
FilterCard filter = new FilterCard("card named \"" + cardName + "\"");
|
||||
filter.add(new NamePredicate(cardName));
|
||||
|
||||
// cards in Graveyard
|
||||
int cardsCount = targetPlayer.getGraveyard().count(filter, game);
|
||||
int cardsCount = Math.min(targetPlayer.getGraveyard().count(filter, game), maxRemaining);
|
||||
if (cardsCount > 0) {
|
||||
filter.setMessage("card named " + cardName + " in the graveyard of " + targetPlayer.getName());
|
||||
TargetCard target = new TargetCard((graveyardExileOptional ? 0 : cardsCount), cardsCount, Zone.GRAVEYARD, filter);
|
||||
target.withNotTarget(true);
|
||||
if (controller.choose(Outcome.Exile, targetPlayer.getGraveyard(), target, source, game)) {
|
||||
maxRemaining -= target.getTargets().size();
|
||||
controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);
|
||||
}
|
||||
}
|
||||
|
||||
// cards in Hand
|
||||
cardsCount = targetPlayer.getHand().count(filter, game);
|
||||
cardsCount = Math.min(targetPlayer.getHand().count(filter, game), maxRemaining);
|
||||
filter.setMessage("card named " + cardName + " in the hand of " + targetPlayer.getName());
|
||||
TargetCard target = new TargetCard(0, cardsCount, Zone.HAND, filter);
|
||||
target.withNotTarget(true);
|
||||
if (controller.choose(Outcome.Exile, targetPlayer.getHand(), target, source, game)) {
|
||||
maxRemaining -= target.getTargets().size();
|
||||
if (drawForEachHandCard) {
|
||||
handCards = target.getTargets().size();
|
||||
}
|
||||
controller.moveCards(new CardsImpl(target.getTargets()), Zone.EXILED, source, game);
|
||||
}
|
||||
|
||||
// cards in Library
|
||||
Cards cardsInLibrary = new CardsImpl();
|
||||
cardsInLibrary.addAllCards(targetPlayer.getLibrary().getCards(game));
|
||||
cardsCount = cardsInLibrary.count(filter, game);
|
||||
cardsCount = Math.min(cardsInLibrary.count(filter, game), maxRemaining);
|
||||
filter.setMessage("card named " + cardName + " in the library of " + targetPlayer.getLogName());
|
||||
TargetCardInLibrary targetLib = new TargetCardInLibrary(0, cardsCount, filter);
|
||||
if (controller.choose(Outcome.Exile, cardsInLibrary, targetLib, source, game)) {
|
||||
controller.moveCards(new CardsImpl(targetLib.getTargets()), Zone.EXILED, source, game);
|
||||
}
|
||||
targetPlayer.shuffleLibrary(source, game);
|
||||
|
||||
if (handCards > 0) {
|
||||
targetPlayer.drawCards(handCards, source, game);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -44,8 +44,14 @@ public class NamePredicate implements Predicate<MageObject> {
|
|||
}
|
||||
// If a player names a card, the player may name either half of a split card, but not both.
|
||||
// A split card has the chosen name if one of its two names matches the chosen name.
|
||||
// Same for modal double faces cards
|
||||
if (input instanceof CardWithHalves) {
|
||||
// This is NOT the same for double faced cards, where only the front side matches
|
||||
|
||||
// Test of Talents ruling:
|
||||
// If the back face of a modal double-faced card is countered, you will not be able to exile any cards,
|
||||
// including the one that you countered, because those cards have only their front-face characteristics
|
||||
// (including name) in the graveyard, hand, and library. (2021-04-16)
|
||||
|
||||
if (input instanceof SplitCard) {
|
||||
return CardUtil.haveSameNames(name, ((CardWithHalves) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
CardUtil.haveSameNames(name, ((CardWithHalves) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) ||
|
||||
CardUtil.haveSameNames(name, input.getName(), this.ignoreMtgRuleForEmptyNames);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue