diff --git a/Mage.Sets/src/mage/cards/s/SorceresssSchemes.java b/Mage.Sets/src/mage/cards/s/SorceresssSchemes.java new file mode 100644 index 00000000000..1b61d54f950 --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SorceresssSchemes.java @@ -0,0 +1,56 @@ +package mage.cards.s; + +import mage.Mana; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.ReturnToHandTargetEffect; +import mage.abilities.effects.mana.BasicManaEffect; +import mage.abilities.keyword.FlashbackAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.AbilityPredicate; +import mage.target.common.TargetCardInYourGraveyardOrExile; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class SorceresssSchemes extends CardImpl { + + private static final FilterCard filter = new FilterCard("exiled card with flashback you own"); + + static { + filter.add(TargetController.YOU.getOwnerPredicate()); + filter.add(new AbilityPredicate(FlashbackAbility.class)); + } + + public SorceresssSchemes(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}"); + + // Return target instant or sorcery card from your graveyard or exiled card with flashback you own to your hand. Add {R}. + this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); + this.getSpellAbility().addTarget( + new TargetCardInYourGraveyardOrExile( + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD, filter + ) + ); + this.getSpellAbility().addEffect(new BasicManaEffect(Mana.RedMana(1))); + + // Flashback {4}{R} + this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{4}{R}"))); + + } + + private SorceresssSchemes(final SorceresssSchemes card) { + super(card); + } + + @Override + public SorceresssSchemes copy() { + return new SorceresssSchemes(this); + } +} diff --git a/Mage.Sets/src/mage/sets/FinalFantasy.java b/Mage.Sets/src/mage/sets/FinalFantasy.java index cf8b6ee6004..ed3bf333a5e 100644 --- a/Mage.Sets/src/mage/sets/FinalFantasy.java +++ b/Mage.Sets/src/mage/sets/FinalFantasy.java @@ -481,6 +481,7 @@ public final class FinalFantasy extends ExpansionSet { cards.add(new SetCardInfo("Sleep Magic", 74, Rarity.UNCOMMON, mage.cards.s.SleepMagic.class)); cards.add(new SetCardInfo("Snow Villiers", 33, Rarity.UNCOMMON, mage.cards.s.SnowVilliers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Snow Villiers", 432, Rarity.UNCOMMON, mage.cards.s.SnowVilliers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sorceress's Schemes", 159, Rarity.UNCOMMON, mage.cards.s.SorceresssSchemes.class)); cards.add(new SetCardInfo("Squall, SeeD Mercenary", 243, Rarity.RARE, mage.cards.s.SquallSeeDMercenary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Squall, SeeD Mercenary", 402, Rarity.RARE, mage.cards.s.SquallSeeDMercenary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Squall, SeeD Mercenary", 509, Rarity.RARE, mage.cards.s.SquallSeeDMercenary.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/SorceresssSchemesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/SorceresssSchemesTest.java new file mode 100644 index 00000000000..427ffd253a0 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/fin/SorceresssSchemesTest.java @@ -0,0 +1,98 @@ +package org.mage.test.cards.single.fin; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class SorceresssSchemesTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.s.SorceresssSchemes Sorceress's Schemes} {3}{R} + * Sorcery + * Return target instant or sorcery card from your graveyard or exiled card with flashback you own to your hand. Add {R}. + * Flashback {4}{R} + */ + private static final String schemes = "Sorceress's Schemes"; + + @Test + public void test_target_in_graveyard() { + addCard(Zone.HAND, playerA, schemes, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); + addCard(Zone.EXILED, playerA, "Deep Analysis", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, schemes, "Lightning Bolt"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, "Lightning Bolt", 1); + } + + @Test + public void test_target_in_exile() { + addCard(Zone.HAND, playerA, schemes, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); + addCard(Zone.EXILED, playerA, "Deep Analysis", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, schemes, "Deep Analysis"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, "Deep Analysis", 1); + } + + @Test + public void test_auto_target_in_graveyard() { + addCard(Zone.HAND, playerA, schemes, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.GRAVEYARD, playerA, "Lightning Bolt", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, schemes); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, "Lightning Bolt", 1); + } + + @Test + public void test_auto_target_in_exile() { + addCard(Zone.HAND, playerA, schemes, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.EXILED, playerA, "Deep Analysis", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, schemes); + + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertHandCount(playerA, "Deep Analysis", 1); + } + + @Test + public void test_various_nontarget() { + addCard(Zone.HAND, playerA, schemes, 1); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + + addCard(Zone.GRAVEYARD, playerA, "Goblin Piker", 1); // not instant/sorcery + addCard(Zone.GRAVEYARD, playerA, "Bloomvine Regent", 1); // omen part not looked at + addCard(Zone.EXILED, playerA, "Lightning Bolt", 1); // doesn't have flashback + addCard(Zone.GRAVEYARD, playerB, "Lightning Bolt", 1); // not in your graveyard + addCard(Zone.EXILED, playerB, "Deep Analysis", 1); // in exile not owned + + checkPlayableAbility("can not cast Schemes", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + schemes, false); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + } +} diff --git a/Mage/src/main/java/mage/target/TargetCard.java b/Mage/src/main/java/mage/target/TargetCard.java index b5322d2782d..e0ef2ba5bfe 100644 --- a/Mage/src/main/java/mage/target/TargetCard.java +++ b/Mage/src/main/java/mage/target/TargetCard.java @@ -56,6 +56,18 @@ public class TargetCard extends TargetObject { return this; } + /** + * Checks if there are enough {@link Card} that can be selected. + * + * @param sourceControllerId - controller of the select event + * @param game + * @return - true if enough valid {@link Card} exist + */ + @Override + public boolean canChoose(UUID sourceControllerId, Game game) { + return canChoose(sourceControllerId, null, game); + } + /** * Checks if there are enough {@link Card cards} in the appropriate zone that the player can choose from among them * or if they are autochosen since there are fewer than the minimum number. @@ -67,7 +79,6 @@ public class TargetCard extends TargetObject { */ @Override public boolean canChoose(UUID sourceControllerId, Ability source, Game game) { - UUID sourceId = source != null ? source.getSourceId() : null; int possibleTargets = 0; if (getMinNumberOfTargets() == 0) { // if 0 target is valid, the canChoose is always true return true; @@ -80,150 +91,245 @@ public class TargetCard extends TargetObject { } switch (zone) { case HAND: - for (Card card : player.getHand().getCards(filter, sourceControllerId, source, game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets++; - if (possibleTargets >= this.minNumberOfTargets) { - return true; - } - } - } + possibleTargets += countPossibleTargetInHand(game, player, sourceControllerId, source, + filter, isNotTarget(), this.minNumberOfTargets - possibleTargets); break; case GRAVEYARD: - for (Card card : player.getGraveyard().getCards(filter, sourceControllerId, source, game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets++; - if (possibleTargets >= this.minNumberOfTargets) { - return true; - } - } - } + possibleTargets += countPossibleTargetInGraveyard(game, player, sourceControllerId, source, + filter, isNotTarget(), this.minNumberOfTargets - possibleTargets); break; case LIBRARY: - for (Card card : player.getLibrary().getUniqueCards(game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - if (filter.match(card, game)) { - possibleTargets++; - if (possibleTargets >= this.minNumberOfTargets) { - return true; - } - } - } - } + possibleTargets += countPossibleTargetInLibrary(game, player, sourceControllerId, source, + filter, isNotTarget(), this.minNumberOfTargets - possibleTargets); break; case EXILED: - for (Card card : game.getExile().getPermanentExile().getCards(game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - if (filter.match(card, player.getId(), game)) { - possibleTargets++; - if (possibleTargets >= this.minNumberOfTargets) { - return true; - } - } - } - } + possibleTargets += countPossibleTargetInExile(game, player, sourceControllerId, source, + filter, isNotTarget(), this.minNumberOfTargets - possibleTargets); break; case COMMAND: - List possibleCards = game.getCommandersIds(player, CommanderCardType.ANY, false).stream() - .map(game::getCard) - .filter(Objects::nonNull) - .filter(card -> game.getState().getZone(card.getId()).equals(Zone.COMMAND)) - .filter(card -> filter.match(card, sourceControllerId, source, game)) - .collect(Collectors.toList()); - for (Card card : possibleCards) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets++; - if (possibleTargets >= this.minNumberOfTargets) { - return true; - } - } - } + possibleTargets += countPossibleTargetInCommandZone(game, player, sourceControllerId, source, + filter, isNotTarget(), this.minNumberOfTargets - possibleTargets); break; } + if (possibleTargets >= this.minNumberOfTargets) { + return true; + } } } return false; } /** - * Checks if there are enough {@link Card} that can be selected. - * - * @param sourceControllerId - controller of the select event - * @param game - * @return - true if enough valid {@link Card} exist + * count up to N possible target cards in a player's hand matching the filter */ - @Override - public boolean canChoose(UUID sourceControllerId, Game game) { - return canChoose(sourceControllerId, null, game); - } - - @Override - public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { - Set possibleTargets = new HashSet<>(); + protected static int countPossibleTargetInHand(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget, int countUpTo) { UUID sourceId = source != null ? source.getSourceId() : null; - for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - switch (zone) { - case HAND: - for (Card card : player.getHand().getCards(filter, sourceControllerId, source, game)) { - // TODO: Why for sourceId == null? - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets.add(card.getId()); - } - } - break; - case GRAVEYARD: - for (Card card : player.getGraveyard().getCards(filter, sourceControllerId, source, game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets.add(card.getId()); - } - } - break; - case LIBRARY: - for (Card card : player.getLibrary().getUniqueCards(game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - if (filter.match(card, sourceControllerId, source, game)) { - possibleTargets.add(card.getId()); - } - } - } - break; - case EXILED: - for (Card card : game.getExile().getPermanentExile().getCards(game)) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - if (filter.match(card, sourceControllerId, source, game)) { - possibleTargets.add(card.getId()); - } - } - } - break; - case COMMAND: - List possibleCards = game.getCommandersIds(player, CommanderCardType.ANY, false).stream() - .map(game::getCard) - .filter(Objects::nonNull) - .filter(card -> game.getState().getZone(card.getId()).equals(Zone.COMMAND)) - .filter(card -> filter.match(card, sourceControllerId, source, game)) - .collect(Collectors.toList()); - for (Card card : possibleCards) { - if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { - possibleTargets.add(card.getId()); - } - } - break; + int possibleTargets = 0; + for (Card card : player.getHand().getCards(filter, sourceControllerId, source, game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets++; + if (possibleTargets >= countUpTo) { + return possibleTargets; // early return for faster computation. } } } return possibleTargets; } + /** + * count up to N possible target cards in a player's graveyard matching the filter + */ + protected static int countPossibleTargetInGraveyard(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget, int countUpTo) { + UUID sourceId = source != null ? source.getSourceId() : null; + int possibleTargets = 0; + for (Card card : player.getGraveyard().getCards(filter, sourceControllerId, source, game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets++; + if (possibleTargets >= countUpTo) { + return possibleTargets; // early return for faster computation. + } + } + } + return possibleTargets; + } + + /** + * count up to N possible target cards in a player's library matching the filter + */ + protected static int countPossibleTargetInLibrary(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget, int countUpTo) { + UUID sourceId = source != null ? source.getSourceId() : null; + int possibleTargets = 0; + for (Card card : player.getLibrary().getUniqueCards(game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + if (filter.match(card, game)) { + possibleTargets++; + if (possibleTargets >= countUpTo) { + return possibleTargets; // early return for faster computation. + } + } + } + } + return possibleTargets; + } + + /** + * count up to N possible target cards in a player's exile matching the filter + */ + protected static int countPossibleTargetInExile(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget, int countUpTo) { + UUID sourceId = source != null ? source.getSourceId() : null; + int possibleTargets = 0; + for (Card card : game.getExile().getPermanentExile().getCards(game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + if (filter.match(card, player.getId(), game)) { + possibleTargets++; + if (possibleTargets >= countUpTo) { + return possibleTargets; // early return for faster computation. + } + } + } + } + return possibleTargets; + } + + /** + * count up to N possible target cards in a player's command zone matching the filter + */ + protected static int countPossibleTargetInCommandZone(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget, int countUpTo) { + UUID sourceId = source != null ? source.getSourceId() : null; + int possibleTargets = 0; + List possibleCards = game.getCommandersIds(player, CommanderCardType.ANY, false).stream() + .map(game::getCard) + .filter(Objects::nonNull) + .filter(card -> game.getState().getZone(card.getId()).equals(Zone.COMMAND)) + .filter(card -> filter.match(card, sourceControllerId, source, game)) + .collect(Collectors.toList()); + for (Card card : possibleCards) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets++; + if (possibleTargets >= countUpTo) { + return possibleTargets; // early return for faster computation. + } + } + } + return possibleTargets; + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Game game) { + return possibleTargets(sourceControllerId, (Ability) null, game); + } + public Set possibleTargets(UUID sourceControllerId, Cards cards, Ability source, Game game) { return cards.getCards(filter, sourceControllerId, source, game).stream().map(MageItem::getId).collect(Collectors.toSet()); } @Override - public Set possibleTargets(UUID sourceControllerId, Game game) { - return possibleTargets(sourceControllerId, (Ability) null, game); + public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { + Set possibleTargets = new HashSet<>(); + for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + switch (zone) { + case HAND: + possibleTargets.addAll(getAllPossibleTargetInHand(game, player, sourceControllerId, source, filter, isNotTarget())); + break; + case GRAVEYARD: + possibleTargets.addAll(getAllPossibleTargetInGraveyard(game, player, sourceControllerId, source, filter, isNotTarget())); + break; + case LIBRARY: + possibleTargets.addAll(getAllPossibleTargetInLibrary(game, player, sourceControllerId, source, filter, isNotTarget())); + break; + case EXILED: + possibleTargets.addAll(getAllPossibleTargetInExile(game, player, sourceControllerId, source, filter, isNotTarget())); + break; + case COMMAND: + possibleTargets.addAll(getAllPossibleTargetInCommandZone(game, player, sourceControllerId, source, filter, isNotTarget())); + break; + } + } + } + return possibleTargets; + } + + /** + * set of all matching target in a player's hand + */ + protected static Set getAllPossibleTargetInHand(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) { + Set possibleTargets = new HashSet<>(); + UUID sourceId = source != null ? source.getSourceId() : null; + for (Card card : player.getHand().getCards(filter, sourceControllerId, source, game)) { + // TODO: Why for sourceId == null? + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets.add(card.getId()); + } + } + return possibleTargets; + } + + /** + * set of all matching target in a player's hand + */ + protected static Set getAllPossibleTargetInGraveyard(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) { + Set possibleTargets = new HashSet<>(); + UUID sourceId = source != null ? source.getSourceId() : null; + for (Card card : player.getGraveyard().getCards(filter, sourceControllerId, source, game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets.add(card.getId()); + } + } + return possibleTargets; + } + + /** + * set of all matching target in a player's hand + */ + protected static Set getAllPossibleTargetInLibrary(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) { + Set possibleTargets = new HashSet<>(); + UUID sourceId = source != null ? source.getSourceId() : null; + for (Card card : player.getLibrary().getUniqueCards(game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + if (filter.match(card, sourceControllerId, source, game)) { + possibleTargets.add(card.getId()); + } + } + } + return possibleTargets; + } + + /** + * set of all matching target for a player in exile + */ + protected static Set getAllPossibleTargetInExile(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) { + Set possibleTargets = new HashSet<>(); + UUID sourceId = source != null ? source.getSourceId() : null; + for (Card card : game.getExile().getPermanentExile().getCards(game)) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + if (filter.match(card, sourceControllerId, source, game)) { + possibleTargets.add(card.getId()); + } + } + } + return possibleTargets; + } + + /** + * set of all matching target in a player's command zone + */ + protected static Set getAllPossibleTargetInCommandZone(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) { + Set possibleTargets = new HashSet<>(); + UUID sourceId = source != null ? source.getSourceId() : null; + List possibleCards = game.getCommandersIds(player, CommanderCardType.ANY, false).stream() + .map(game::getCard) + .filter(Objects::nonNull) + .filter(card -> game.getState().getZone(card.getId()).equals(Zone.COMMAND)) + .filter(card -> filter.match(card, sourceControllerId, source, game)) + .collect(Collectors.toList()); + for (Card card : possibleCards) { + if (sourceId == null || isNotTarget || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) { + possibleTargets.add(card.getId()); + } + } + return possibleTargets; } // TODO: check all class targets, if it override canTarget then make sure it override ALL 3 METHODS with canTarget and possibleTargets (method with cards doesn't need) diff --git a/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyardOrExile.java b/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyardOrExile.java new file mode 100644 index 00000000000..fd5b6816627 --- /dev/null +++ b/Mage/src/main/java/mage/target/common/TargetCardInYourGraveyardOrExile.java @@ -0,0 +1,104 @@ +package mage.target.common; + +import mage.abilities.Ability; +import mage.cards.Card; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author Susucr + */ +public class TargetCardInYourGraveyardOrExile extends TargetCard { + + private final FilterCard filterGraveyard; + private final FilterCard filterExile; + + public TargetCardInYourGraveyardOrExile(FilterCard filterGraveyard, FilterCard filterExile) { + super(1, 1, Zone.GRAVEYARD, filterGraveyard); // we do not actually use those, as all is overwritten + this.filterGraveyard = filterGraveyard; + this.filterExile = filterExile; + this.targetName = filterGraveyard.getMessage() + " or " + filterExile.getMessage(); + } + + protected TargetCardInYourGraveyardOrExile(final TargetCardInYourGraveyardOrExile target) { + super(target); + this.filterGraveyard = target.filterGraveyard; + this.filterExile = target.filterExile; + } + + @Override + public boolean canChoose(UUID sourceControllerId, Ability source, Game game) { + Player player = game.getPlayer(sourceControllerId); + if (player == null) { + return false; + } + int possibleTargets = 0; + // in your graveyard: + possibleTargets += countPossibleTargetInGraveyard(game, player, sourceControllerId, source, + filterGraveyard, isNotTarget(), this.minNumberOfTargets - possibleTargets); + if (possibleTargets >= this.minNumberOfTargets) { + return true; + } + // in exile: + possibleTargets += countPossibleTargetInExile(game, player, sourceControllerId, source, + filterExile, isNotTarget(), this.minNumberOfTargets - possibleTargets); + return possibleTargets >= this.minNumberOfTargets; + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Game game) { + return this.possibleTargets(sourceControllerId, (Ability) null, game); + } + + @Override + public Set possibleTargets(UUID sourceControllerId, Ability source, Game game) { + Set possibleTargets = new HashSet<>(); + Player player = game.getPlayer(sourceControllerId); + if (player == null) { + return possibleTargets; + } + // in your graveyard: + possibleTargets.addAll(getAllPossibleTargetInGraveyard(game, player, sourceControllerId, source, filterGraveyard, isNotTarget())); + // in exile: + possibleTargets.addAll(getAllPossibleTargetInExile(game, player, sourceControllerId, source, filterExile, isNotTarget())); + return possibleTargets; + } + + @Override + public boolean canTarget(UUID id, Ability source, Game game) { + return this.canTarget(source.getControllerId(), id, source, game); + } + + @Override + public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) { + Card card = game.getCard(id); + if (card == null) { + return false; + } + switch (game.getState().getZone(id)) { + case GRAVEYARD: + return filterGraveyard.match(card, playerId, source, game); + case EXILED: + return filterExile.match(card, playerId, source, game); + default: + return false; + } + } + + @Override + public boolean canTarget(UUID id, Game game) { + return this.canTarget(null, id, null, game); + } + + @Override + public TargetCardInYourGraveyardOrExile copy() { + return new TargetCardInYourGraveyardOrExile(this); + } +}