diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 172485b99ff..6fd185ad7b5 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -2986,7 +2986,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { @Override public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) { - Map useable = PlayerImpl.getCastableSpellAbilities(game, this.getId(), card, game.getState().getZone(card.getId()), noMana); + Map useable = PlayerImpl.getCastableSpellAbilities(game, this.getId(), card, game.getState().getZone(card.getId()), noMana); return (SpellAbility) useable.values().stream().findFirst().orElse(null); } diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 9b041f95b19..e1898cbe152 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -2185,10 +2185,10 @@ public class HumanPlayer extends PlayerImpl { MageObject object = game.getObject(card.getId()); // must be object to find real abilities (example: commander) if (object != null) { String message = "Choose ability to cast" + (noMana ? " for FREE" : "") + "
" + object.getLogName(); - LinkedHashMap useableAbilities = PlayerImpl.getCastableSpellAbilities(game, playerId, object, game.getState().getZone(object.getId()), noMana); + LinkedHashMap useableAbilities = PlayerImpl.getCastableSpellAbilities(game, playerId, object, game.getState().getZone(object.getId()), noMana); if (useableAbilities != null && useableAbilities.size() == 1) { - return (SpellAbility) useableAbilities.values().iterator().next(); + return useableAbilities.values().iterator().next(); } else if (useableAbilities != null && !useableAbilities.isEmpty()) { @@ -2202,7 +2202,7 @@ public class HumanPlayer extends PlayerImpl { UUID responseId = getFixedResponseUUID(game); if (responseId != null) { if (useableAbilities.containsKey(responseId)) { - return (SpellAbility) useableAbilities.get(responseId); + return useableAbilities.get(responseId); } } } diff --git a/Mage.Sets/src/mage/cards/a/AetherworksMarvel.java b/Mage.Sets/src/mage/cards/a/AetherworksMarvel.java index 5669bb4ebd0..6f39ece334e 100644 --- a/Mage.Sets/src/mage/cards/a/AetherworksMarvel.java +++ b/Mage.Sets/src/mage/cards/a/AetherworksMarvel.java @@ -1,8 +1,5 @@ package mage.cards.a; -import java.util.Set; -import java.util.UUID; -import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.common.PutIntoGraveFromBattlefieldAllTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; @@ -10,20 +7,22 @@ import mage.abilities.costs.common.PayEnergyCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.GetEnergyCountersControllerEffect; -import mage.cards.*; +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.common.FilterControlledPermanent; -import mage.filter.common.FilterNonlandCard; +import mage.filter.StaticFilters; 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; /** - * * @author emerald000 */ public final class AetherworksMarvel extends CardImpl { @@ -35,13 +34,13 @@ public final class AetherworksMarvel extends CardImpl { // Whenever a permanent you control is put into a graveyard, you get {E}. this.addAbility(new PutIntoGraveFromBattlefieldAllTriggeredAbility( new GetEnergyCountersControllerEffect(1), false, - new FilterControlledPermanent("a permanent you control"), false)); + StaticFilters.FILTER_CONTROLLED_A_PERMANENT, false + )); // {T}, Pay {E}{E}{E}{E}{E}{E}: Look at the top six cards of your library. // You may cast a card from among them without paying its mana cost. // Put the rest on the bottom of your library in a random order. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new AetherworksMarvelEffect(), new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(new AetherworksMarvelEffect(), new TapSourceCost()); ability.addCost(new PayEnergyCost(6)); this.addAbility(ability); } @@ -78,26 +77,13 @@ class AetherworksMarvelEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Set cardsSet = controller.getLibrary().getTopCards(game, 6); - Cards cards = new CardsImpl(cardsSet); - TargetCard target = new TargetCardInLibrary(0, 1, - new FilterNonlandCard("card to cast without paying its mana cost")); - if (controller.choose(Outcome.PlayForFree, cards, target, game)) { - Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - if (cardWasCast) { - cards.remove(card); - } - } - } - controller.putCardsOnBottomOfLibrary(cards, game, source, false); - return true; + if (controller == null) { + return false; } - return false; + Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 6)); + CardUtil.castSpellWithAttributesForFree(controller, source, game, cards, StaticFilters.FILTER_CARD); + cards.retainZone(Zone.LIBRARY, game); + controller.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; } } diff --git a/Mage.Sets/src/mage/cards/a/ArcaneEndeavor.java b/Mage.Sets/src/mage/cards/a/ArcaneEndeavor.java index 70f26c348c8..2d2cd938152 100644 --- a/Mage.Sets/src/mage/cards/a/ArcaneEndeavor.java +++ b/Mage.Sets/src/mage/cards/a/ArcaneEndeavor.java @@ -1,17 +1,19 @@ package mage.cards.a; import mage.abilities.Ability; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; +import mage.util.CardUtil; import java.util.List; import java.util.UUID; @@ -40,10 +42,6 @@ public final class ArcaneEndeavor extends CardImpl { class ArcaneEndeavorEffect extends OneShotEffect { - private static final FilterCard filter = new FilterInstantOrSorceryCard( - "instant or sorcery card with mana value %mv or less from your hand" - ); - ArcaneEndeavorEffect() { super(Outcome.Benefit); staticText = "roll two d8 and choose one result. Draw cards equal to that result. " + @@ -82,7 +80,9 @@ class ArcaneEndeavorEffect extends OneShotEffect { second = firstResult; } player.drawCards(first, source, game); - new CastWithoutPayingManaCostEffect(StaticValue.get(second), filter).apply(game, source); + FilterCard filter = new FilterInstantOrSorceryCard(); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, second + 1)); + CardUtil.castSpellWithAttributesForFree(player, source, game, new CardsImpl(player.getHand()), filter); return true; } } diff --git a/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java b/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java index b9bf0eee652..1260aad0644 100644 --- a/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java +++ b/Mage.Sets/src/mage/cards/a/AshiokNightmareMuse.java @@ -1,23 +1,26 @@ package mage.cards.a; -import java.util.UUID; -import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.FilterCard; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.card.FaceDownPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.permanent.token.AshiokNightmareMuseToken; import mage.players.Player; -import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInHand; import mage.target.common.TargetNonlandPermanent; +import mage.util.CardUtil; + +import java.util.UUID; /** * @author TheElk801 @@ -95,6 +98,7 @@ class AshiokNightmareMuseCastEffect extends OneShotEffect { static { filter.add(TargetController.OPPONENT.getOwnerPredicate()); + filter.add(Predicates.not(FaceDownPredicate.instance)); } AshiokNightmareMuseCastEffect() { @@ -117,25 +121,10 @@ class AshiokNightmareMuseCastEffect extends OneShotEffect { if (controller == null) { return false; } - TargetCardInExile target = new TargetCardInExile(0, 3, filter, null); - target.setNotTarget(true); - if (!controller.chooseTarget(outcome, target, source, game)) { // method is fine, controller is still choosing the card - return false; - } - for (UUID targetId : target.getTargets()) { - if (targetId != null) { - Card chosenCard = game.getCard(targetId); - if (chosenCard != null - && game.getState().getZone(chosenCard.getId()) == Zone.EXILED // must be exiled - && game.getOpponents(controller.getId()).contains(chosenCard.getOwnerId()) // must be owned by an opponent - && controller.chooseUse(outcome, "Cast " + chosenCard.getName() + " without paying its mana cost?", source, game)) { - game.getState().setValue("PlayFromNotOwnHandZone" + chosenCard.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(chosenCard, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + chosenCard.getId(), null); - } - } - } + CardUtil.castMultipleWithAttributeForFree( + controller, source, game, new CardsImpl(game.getExile().getCards(filter, game)), + StaticFilters.FILTER_CARD, 3 + ); return true; } } diff --git a/Mage.Sets/src/mage/cards/b/BaralsExpertise.java b/Mage.Sets/src/mage/cards/b/BaralsExpertise.java index 634b7ee170f..483f3a0a59a 100644 --- a/Mage.Sets/src/mage/cards/b/BaralsExpertise.java +++ b/Mage.Sets/src/mage/cards/b/BaralsExpertise.java @@ -1,27 +1,34 @@ package mage.cards.b; -import java.util.UUID; import mage.abilities.effects.common.ReturnToHandTargetEffect; -import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class BaralsExpertise extends CardImpl { private static final FilterPermanent filter = new FilterPermanent("artifacts and/or creatures"); + private static final FilterCard filter2 = new FilterCard("a spell with mana value 4 or less"); static { - filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), - CardType.CREATURE.getPredicate())); + filter.add(Predicates.or( + CardType.ARTIFACT.getPredicate(), + CardType.CREATURE.getPredicate() + )); + filter2.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); } public BaralsExpertise(UUID ownerId, CardSetInfo setInfo) { @@ -32,7 +39,7 @@ public final class BaralsExpertise extends CardImpl { getSpellAbility().addTarget(new TargetPermanent(0, 3, filter, false)); // You may cast a card with converted mana cost 4 or less from your hand without paying its mana cost. - getSpellAbility().addEffect(new CastWithoutPayingManaCostEffect(4).concatBy("
")); + getSpellAbility().addEffect(new CastFromHandForFreeEffect(filter2).concatBy("
")); } private BaralsExpertise(final BaralsExpertise card) { diff --git a/Mage.Sets/src/mage/cards/b/BrainInAJar.java b/Mage.Sets/src/mage/cards/b/BrainInAJar.java index f2362fcefd7..c383abea1fa 100644 --- a/Mage.Sets/src/mage/cards/b/BrainInAJar.java +++ b/Mage.Sets/src/mage/cards/b/BrainInAJar.java @@ -1,7 +1,5 @@ package mage.cards.b; -import java.util.UUID; -import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.RemoveVariableCountersSourceCost; @@ -10,13 +8,11 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.dynamicvalue.common.RemovedCountersForCostValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; @@ -24,10 +20,11 @@ import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class BrainInAJar extends CardImpl { @@ -39,20 +36,17 @@ public final class BrainInAJar extends CardImpl { // cast an instant or sorcery card with converted mana costs equal // to the number of charge counters on Brain in a Jar from your // hand without paying its mana cost. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), - new GenericManaCost(1)); + Ability ability = new SimpleActivatedAbility( + new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), new GenericManaCost(1) + ); ability.addEffect(new BrainInAJarCastEffect()); ability.addCost(new TapSourceCost()); this.addAbility(ability); // {3}, {T}, Remove X charge counters from Brain in a Jar: Scry X. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new BrainInAJarScryEffect(), - new GenericManaCost(3)); + ability = new SimpleActivatedAbility(new BrainInAJarScryEffect(), new GenericManaCost(3)); ability.addCost(new TapSourceCost()); - ability.addCost(new RemoveVariableCountersSourceCost( - CounterType.CHARGE.createInstance())); + ability.addCost(new RemoveVariableCountersSourceCost(CounterType.CHARGE.createInstance())); this.addAbility(ability); } @@ -87,32 +81,14 @@ class BrainInAJarCastEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller != null - && sourceObject != null) { - int counters = sourceObject.getCounters(game).getCount(CounterType.CHARGE); - FilterCard filter = new FilterInstantOrSorceryCard(); - filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, counters)); - int cardsToCast = controller.getHand().count(filter, source.getControllerId(), - source.getSourceId(), game); - if (cardsToCast > 0 - && controller.chooseUse(Outcome.PlayForFree, - "Cast an instant or sorcery card with mana values of " - + counters + " from your hand without paying its mana cost?", - source, game)) { - TargetCardInHand target = new TargetCardInHand(filter); - controller.chooseTarget(outcome, target, source, game); - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - } - } - return true; + Permanent sourceObject = source.getSourcePermanentOrLKI(game); + if (controller == null || sourceObject == null) { + return false; } - return false; + int counters = sourceObject.getCounters(game).getCount(CounterType.CHARGE); + FilterCard filter = new FilterInstantOrSorceryCard(); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, counters)); + return CardUtil.castSpellWithAttributesForFree(controller, source, game, controller.getHand(), filter); } } diff --git a/Mage.Sets/src/mage/cards/c/CecilyHauntedMage.java b/Mage.Sets/src/mage/cards/c/CecilyHauntedMage.java index 8899e38c863..d37f882a645 100644 --- a/Mage.Sets/src/mage/cards/c/CecilyHauntedMage.java +++ b/Mage.Sets/src/mage/cards/c/CecilyHauntedMage.java @@ -1,24 +1,19 @@ package mage.cards.c; -import mage.ApprovingObject; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; -import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; import mage.abilities.keyword.FriendsForeverAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetCardInHand; -import org.apache.log4j.Logger; +import mage.util.CardUtil; import java.util.UUID; @@ -83,45 +78,10 @@ class CecilyHauntedMageEffect extends OneShotEffect { } player.drawCards(1, source, game); player.loseLife(1, game, source, false); - if (player.getHand().size() < 11) { - return true; - } - // TODO: change this to fit with changes made in https://github.com/magefree/mage/pull/8136 when merged - Target target = new TargetCardInHand(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); - if (!target.canChoose( - source.getSourceId(), player.getId(), game - ) || !player.chooseUse( - Outcome.PlayForFree, "Cast an instant or sorcery spell " + - "from your hand without paying its mana cost?", source, game - )) { - return true; - } - Card cardToCast = null; - boolean cancel = false; - while (player.canRespond() - && !cancel) { - if (player.chooseTarget(Outcome.PlayForFree, target, source, game)) { - cardToCast = game.getCard(target.getFirstTarget()); - if (cardToCast != null) { - if (cardToCast.getSpellAbility() == null) { - Logger.getLogger(CastWithoutPayingManaCostEffect.class).fatal("Card: " - + cardToCast.getName() + " is no land and has no spell ability!"); - cancel = true; - } - if (cardToCast.getSpellAbility().canChooseTarget(game, player.getId())) { - cancel = true; - } - } - } else { - cancel = true; - } - } - if (cardToCast != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), Boolean.TRUE); - player.cast(player.chooseAbilityForCast(cardToCast, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), null); - } - return true; + return player.getHand().size() < 11 + || CardUtil.castSpellWithAttributesForFree( + player, source, game, player.getHand().copy(), + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY + ); } } diff --git a/Mage.Sets/src/mage/cards/c/ChandraAblaze.java b/Mage.Sets/src/mage/cards/c/ChandraAblaze.java index 9dbc678a2da..0b607a83a26 100644 --- a/Mage.Sets/src/mage/cards/c/ChandraAblaze.java +++ b/Mage.Sets/src/mage/cards/c/ChandraAblaze.java @@ -10,23 +10,20 @@ import mage.abilities.effects.common.discard.DiscardHandAllEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; +import mage.cards.CardsImpl; +import mage.constants.*; import mage.filter.FilterCard; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; +import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetAnyTarget; -import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetDiscard; +import mage.util.CardUtil; -import java.util.Set; import java.util.UUID; -import mage.ApprovingObject; /** * @author North @@ -138,6 +135,12 @@ class ChandraAblazeEffect2 extends OneShotEffect { class ChandraAblazeEffect5 extends OneShotEffect { + private static final FilterCard filter = new FilterInstantOrSorceryCard(); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + public ChandraAblazeEffect5() { super(Outcome.PlayForFree); this.staticText = "Cast any number of red instant and/or sorcery cards from your graveyard without paying their mana costs"; @@ -154,33 +157,17 @@ class ChandraAblazeEffect5 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { + // Under this card's current oracle wording, it only casts red instant or sorcery cards + // This may have been a mistake which could change in the future Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - FilterCard filter = new FilterCard("red instant or sorcery card from your graveyard to play"); - filter.add(new ColorPredicate(ObjectColor.RED)); - filter.add(Predicates.or( - CardType.INSTANT.getPredicate(), - CardType.SORCERY.getPredicate())); - - String message = "Play red instant or sorcery card from your graveyard without paying its mana cost?"; - Set cards = player.getGraveyard().getCards(filter, game); - TargetCardInGraveyard target = new TargetCardInGraveyard(filter); - while (!cards.isEmpty() && player.chooseUse(outcome, message, source, game)) { - target.clearChosen(); - if (player.choose(outcome, target, source.getSourceId(), game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - player.cast(player.chooseAbilityForCast(card, game, true), game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - - cards.remove(card); - } - } - } - - return true; + if (player == null) { + return false; } - return false; + CardUtil.castMultipleWithAttributeForFree( + player, source, game, + new CardsImpl(player.getGraveyard().getCards(filter, game)), + StaticFilters.FILTER_CARD + ); + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CollectedConjuring.java b/Mage.Sets/src/mage/cards/c/CollectedConjuring.java index f46fe78b9d4..b838db5ce86 100644 --- a/Mage.Sets/src/mage/cards/c/CollectedConjuring.java +++ b/Mage.Sets/src/mage/cards/c/CollectedConjuring.java @@ -1,10 +1,11 @@ package mage.cards.c; -import mage.ApprovingObject; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Outcome; @@ -13,7 +14,7 @@ import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; +import mage.util.CardUtil; import java.util.UUID; @@ -51,12 +52,6 @@ class CollectedConjuringEffect extends OneShotEffect { filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); } - private static final FilterCard filter2 = filter.copy(); - - static { - filter2.setMessage("sorcery card with mana value 3 or less"); - } - CollectedConjuringEffect() { super(Outcome.PlayForFree); this.staticText = "exile the top six cards of your library. You may cast up to two sorcery spells " + @@ -76,42 +71,12 @@ class CollectedConjuringEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller == null - || sourceObject == null) { + if (controller == null) { return false; } Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 6)); - Cards cardsToChoose = new CardsImpl(cards); controller.moveCards(cards, Zone.EXILED, source, game); - int cardsCast = 0; - while (!cardsToChoose.getCards(filter, source.getSourceId(), source.getControllerId(), game).isEmpty() - && cardsCast < 2) { - if (!controller.chooseUse(Outcome.PlayForFree, "Cast a card exiled with " - + sourceObject.getLogName() + " without paying its mana cost?", source, game)) { - break; - } - TargetCard targetCard = new TargetCard(1, Zone.EXILED, filter2); - if (!controller.choose(Outcome.PlayForFree, cardsToChoose, targetCard, game)) { - continue; - } - Card card = game.getCard(targetCard.getFirstTarget()); - if (card == null) { - continue; - } - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - cardsToChoose.remove(card); // remove on non cast too (infinite freeze fix) - if (cardWasCast) { - cards.remove(card); - cardsCast++; - } else { - game.informPlayer(controller, "You're not able to cast " - + card.getIdName() + " or you canceled the casting."); - } - } + CardUtil.castMultipleWithAttributeForFree(controller, source, game, cards, filter, 2); controller.putCardsOnBottomOfLibrary(cards, game, source, false); return true; } diff --git a/Mage.Sets/src/mage/cards/c/Counterlash.java b/Mage.Sets/src/mage/cards/c/Counterlash.java index 2e7eda53d13..458d03b8b14 100644 --- a/Mage.Sets/src/mage/cards/c/Counterlash.java +++ b/Mage.Sets/src/mage/cards/c/Counterlash.java @@ -1,15 +1,11 @@ package mage.cards.c; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import mage.ApprovingObject; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.filter.FilterCard; @@ -19,10 +15,13 @@ import mage.game.Game; import mage.game.stack.StackObject; import mage.players.Player; import mage.target.TargetSpell; -import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; + +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; /** - * * @author BetaSteward */ public final class Counterlash extends CardImpl { @@ -50,9 +49,9 @@ class CounterlashEffect extends OneShotEffect { public CounterlashEffect() { super(Outcome.Detriment); - this.staticText = "Counter target spell. You may cast a nonland " - + "card in your hand that shares a card type with that " - + "spell without paying its mana cost"; + this.staticText = "Counter target spell. You may cast a spell " + + "that shares a card type with it from your hand " + + "without paying its mana cost"; } public CounterlashEffect(final CounterlashEffect effect) { @@ -68,32 +67,21 @@ class CounterlashEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); Player controller = game.getPlayer(source.getControllerId()); - if (stackObject != null - && controller != null) { - game.getStack().counter(source.getFirstTarget(), source, game); - if (controller.chooseUse(Outcome.PlayForFree, "Cast a nonland card in your hand that " - + "shares a card type with that spell without paying its mana cost?", source, game)) { - FilterCard filter = new FilterCard(); - List> types = new ArrayList<>(); - for (CardType type : stackObject.getCardType(game)) { - if (type != CardType.LAND) { - types.add(type.getPredicate()); - } - } - filter.add(Predicates.or(types)); - TargetCardInHand target = new TargetCardInHand(filter); - if (controller.choose(Outcome.PutCardInPlay, target, source.getSourceId(), game)) { - Card card = controller.getHand().get(target.getFirstTarget(), game); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - } - } - } + if (stackObject == null || controller == null) { + return false; + } + Set> predicates = stackObject + .getCardType(game) + .stream() + .map(CardType::getPredicate) + .collect(Collectors.toSet()); + game.getStack().counter(source.getFirstTarget(), source, game); + if (predicates.isEmpty()) { return true; } - return false; + FilterCard filter = new FilterCard(); + filter.add(Predicates.or(predicates)); + CardUtil.castSpellWithAttributesForFree(controller, source, game, new CardsImpl(controller.getHand()), filter); + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CovetedPrize.java b/Mage.Sets/src/mage/cards/c/CovetedPrize.java index 5fd841994c9..95a0aee9705 100644 --- a/Mage.Sets/src/mage/cards/c/CovetedPrize.java +++ b/Mage.Sets/src/mage/cards/c/CovetedPrize.java @@ -4,14 +4,17 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.FullPartyCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.dynamicvalue.common.PartyCount; -import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.abilities.hint.common.PartyCountHint; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.common.TargetCardInLibrary; import java.util.UUID; @@ -21,6 +24,12 @@ import java.util.UUID; */ public final class CovetedPrize extends CardImpl { + private static final FilterCard filter = new FilterCard("a spell with mana value 4 or less"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5)); + } + public CovetedPrize(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{B}"); @@ -32,7 +41,7 @@ public final class CovetedPrize extends CardImpl { // Search your library for a card, put it into your hand, then shuffle your library. If you have a full party, you may cast a spell with converted mana cost 4 or less from your hand without paying its mana cost. this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary())); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( - new CastWithoutPayingManaCostEffect(4), + new CastFromHandForFreeEffect(filter), FullPartyCondition.instance, "If you have a full party, " + "you may cast a spell with mana value 4 or less from your hand without paying its mana cost." )); diff --git a/Mage.Sets/src/mage/cards/d/DiscoverTheImpossible.java b/Mage.Sets/src/mage/cards/d/DiscoverTheImpossible.java new file mode 100644 index 00000000000..596e9ffa5ef --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DiscoverTheImpossible.java @@ -0,0 +1,91 @@ +package mage.cards.d; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +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; + +/** + * @author TheElk801 + */ +public final class DiscoverTheImpossible extends CardImpl { + + public DiscoverTheImpossible(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}"); + + // Look at the top five cards of your library. Exile one of them face down and put the rest on the bottom of your library in a random order. You may cast the exiled card without paying its mana cost if it's an instant spell with mana value 2 or less. If you don't, put that card into your hand. + this.getSpellAbility().addEffect(new DiscoverTheImpossibleEffect()); + } + + private DiscoverTheImpossible(final DiscoverTheImpossible card) { + super(card); + } + + @Override + public DiscoverTheImpossible copy() { + return new DiscoverTheImpossible(this); + } +} + +class DiscoverTheImpossibleEffect extends OneShotEffect { + + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); + } + + DiscoverTheImpossibleEffect() { + super(Outcome.PlayForFree); + staticText = "look at the top five cards of your library. Exile one of them face down " + + "and put the rest on the bottom of your library in a random order. " + + "You may cast the exiled card without paying its mana cost if it's an instant spell " + + "with mana value 2 or less. If you don't, put that card into your hand"; + } + + private DiscoverTheImpossibleEffect(final DiscoverTheImpossibleEffect effect) { + super(effect); + } + + @Override + public DiscoverTheImpossibleEffect copy() { + return new DiscoverTheImpossibleEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 5)); + if (cards.isEmpty()) { + return false; + } + TargetCard target = new TargetCardInLibrary(); + player.choose(outcome, cards, target, game); + Card card = game.getCard(target.getFirstTarget()); + if (card == null) { + player.putCardsOnBottomOfLibrary(card, game, source, false); + return true; + } + player.moveCards(card, Zone.EXILED, source, game); + card.setFaceDown(true, game); + cards.retainZone(Zone.LIBRARY, game); + player.putCardsOnBottomOfLibrary(cards, game, source, false); + return CardUtil.castSpellWithAttributesForFree(player, source, game, new CardsImpl(card), filter) + || player.moveCards(card, Zone.HAND, source, game); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DragonKamisEgg.java b/Mage.Sets/src/mage/cards/d/DragonKamisEgg.java new file mode 100644 index 00000000000..ec980a36827 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DragonKamisEgg.java @@ -0,0 +1,91 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DiesThisOrAnotherCreatureTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +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.SubType; +import mage.counters.CounterType; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DragonKamisEgg extends CardImpl { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.DRAGON); + + public DragonKamisEgg(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, ""); + + this.subtype.add(SubType.EGG); + this.power = new MageInt(0); + this.toughness = new MageInt(1); + this.color.setGreen(true); + this.nightCard = true; + + // Whenever Dragon-Kami's Egg or a Dragon you control dies, you may cast a creature spell from among cards you own in exile with hatching counters on them without paying its mana cost. + this.addAbility(new DiesThisOrAnotherCreatureTriggeredAbility( + new DragonKamisEggEffect(), false, filter + ).setTriggerPhrase("Whenever {this} or a Dragon you control dies, ")); + } + + private DragonKamisEgg(final DragonKamisEgg card) { + super(card); + } + + @Override + public DragonKamisEgg copy() { + return new DragonKamisEgg(this); + } +} + +class DragonKamisEggEffect extends OneShotEffect { + + DragonKamisEggEffect() { + super(Outcome.Benefit); + staticText = "you may cast a creature spell from among cards you own in exile " + + "with hatching counters on them without paying its mana cost"; + } + + private DragonKamisEggEffect(final DragonKamisEggEffect effect) { + super(effect); + } + + @Override + public DragonKamisEggEffect copy() { + return new DragonKamisEggEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(); + game.getExile() + .getAllCards(game, player.getId()) + .stream() + .filter(Objects::nonNull) + .filter(card -> card.getCounters(game).containsKey(CounterType.HATCHLING)) + .forEach(cards::add); + return !cards.isEmpty() && CardUtil.castSpellWithAttributesForFree( + player, source, game, cards, StaticFilters.FILTER_CARD_CREATURE + ); + } +} diff --git a/Mage.Sets/src/mage/cards/e/Electrodominance.java b/Mage.Sets/src/mage/cards/e/Electrodominance.java index ede24d12435..39a61cb31c7 100644 --- a/Mage.Sets/src/mage/cards/e/Electrodominance.java +++ b/Mage.Sets/src/mage/cards/e/Electrodominance.java @@ -1,12 +1,20 @@ package mage.cards.e; +import mage.abilities.Ability; import mage.abilities.dynamicvalue.common.ManacostVariableValue; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.players.Player; import mage.target.common.TargetAnyTarget; +import mage.util.CardUtil; import java.util.UUID; @@ -21,7 +29,7 @@ public final class Electrodominance extends CardImpl { // Electrodominance deals X damage to any target. You may cast a card with converted mana cost X or less from your hand without paying its mana cost. this.getSpellAbility().addEffect(new DamageTargetEffect(ManacostVariableValue.REGULAR)); this.getSpellAbility().addTarget(new TargetAnyTarget()); - this.getSpellAbility().addEffect(new CastWithoutPayingManaCostEffect(ManacostVariableValue.REGULAR)); + this.getSpellAbility().addEffect(new ElectrodominanceEffect()); } private Electrodominance(final Electrodominance card) { @@ -33,3 +41,34 @@ public final class Electrodominance extends CardImpl { return new Electrodominance(this); } } + +class ElectrodominanceEffect extends OneShotEffect { + + ElectrodominanceEffect() { + super(Outcome.Benefit); + staticText = "You may cast a spell with mana value X " + + "or less from your hand without paying its mana cost"; + } + + private ElectrodominanceEffect(final ElectrodominanceEffect effect) { + super(effect); + } + + @Override + public ElectrodominanceEffect copy() { + return new ElectrodominanceEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + FilterCard filter = new FilterCard(); + filter.add(new ManaValuePredicate( + ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1 + )); + return CardUtil.castSpellWithAttributesForFree(controller, source, game, controller.getHand(), filter); + } +} diff --git a/Mage.Sets/src/mage/cards/e/EpicExperiment.java b/Mage.Sets/src/mage/cards/e/EpicExperiment.java index c0fe068149b..f168b0d8a92 100644 --- a/Mage.Sets/src/mage/cards/e/EpicExperiment.java +++ b/Mage.Sets/src/mage/cards/e/EpicExperiment.java @@ -3,7 +3,10 @@ package mage.cards.e; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Outcome; @@ -11,13 +14,11 @@ import mage.constants.Zone; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; +import mage.util.CardUtil; import java.util.UUID; -import mage.ApprovingObject; /** * @author LevelX2 @@ -61,59 +62,16 @@ class EpicExperimentEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - // move cards from library to exile - controller.moveCardsToExile(controller.getLibrary().getTopCards(game, - source.getManaCostsToPay().getX()), source, game, true, - source.getSourceId(), sourceObject.getIdName()); - // cast the possible cards without paying the mana - ExileZone epicExperimentExileZone = game.getExile().getExileZone(source.getSourceId()); - FilterCard filter = new FilterInstantOrSorceryCard(); - filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, - source.getManaCostsToPay().getX() + 1)); - filter.setMessage("instant and sorcery cards with mana value " - + source.getManaCostsToPay().getX() + " or less"); - Cards cardsToCast = new CardsImpl(); - if (epicExperimentExileZone == null) { - return true; - } - cardsToCast.addAll(epicExperimentExileZone.getCards(filter, source.getSourceId(), - source.getControllerId(), game)); - while (controller.canRespond() && !cardsToCast.isEmpty()) { - if (!controller.chooseUse(Outcome.PlayForFree, "Cast (another) a card exiled with " - + sourceObject.getLogName() + " without paying its mana cost?", source, game)) { - break; - } - TargetCard targetCard = new TargetCard(1, Zone.EXILED, new FilterCard( - "instant or sorcery card to cast for free")); - if (controller.choose(Outcome.PlayForFree, cardsToCast, targetCard, game)) { - Card card = game.getCard(targetCard.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - if (!cardWasCast) { - game.informPlayer(controller, "You're not able to cast " - + card.getIdName() + " or you canceled the casting."); - } - cardsToCast.remove(card); - } else { - break; - } - } else { - break; - } - } - // move cards not cast to graveyard - ExileZone exileZone = game.getExile().getExileZone(source.getSourceId()); - if (exileZone != null) { - controller.moveCards(exileZone.getCards(game), Zone.GRAVEYARD, source, game); - } - return true; + if (controller == null || sourceObject == null) { + return false; } - - return false; + Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, source.getManaCostsToPay().getX())); + controller.moveCards(cards, Zone.EXILED, source, game); + FilterCard filter = new FilterInstantOrSorceryCard(); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); + CardUtil.castMultipleWithAttributeForFree(controller, source, game, cards, filter); + controller.moveCards(cards, Zone.GRAVEYARD, source, game); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/e/EtaliPrimalStorm.java b/Mage.Sets/src/mage/cards/e/EtaliPrimalStorm.java index de743e3de92..b5ad1da1579 100644 --- a/Mage.Sets/src/mage/cards/e/EtaliPrimalStorm.java +++ b/Mage.Sets/src/mage/cards/e/EtaliPrimalStorm.java @@ -1,22 +1,20 @@ package mage.cards.e; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.*; -import mage.filter.FilterCard; import mage.filter.common.FilterNonlandCard; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; +import mage.util.CardUtil; -import java.util.HashSet; -import java.util.Set; import java.util.UUID; -import mage.ApprovingObject; /** * @author ciaccona007 & L_J @@ -34,7 +32,6 @@ public final class EtaliPrimalStorm extends CardImpl { // Whenever Etali, Primal Storm attacks, exile the top card of each player's library, // then you may cast any number of nonland cards exiled this way without paying their mana costs. this.addAbility(new AttacksTriggeredAbility(new EtaliPrimalStormEffect(), false)); - } private EtaliPrimalStorm(final EtaliPrimalStorm card) { @@ -69,58 +66,19 @@ class EtaliPrimalStormEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null - && sourceObject != null) { - // move cards from library to exile - Set currentExiledCards = new HashSet<>(); - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - Player player = game.getPlayer(playerId); - if (player != null) { - if (!player.getLibrary().getTopCards(game, 1).isEmpty()) { - Card topCard = player.getLibrary().getFromTop(game); - if (topCard != null) { - if (filter.match(topCard, source.getSourceId(), source.getControllerId(), game)) { - currentExiledCards.add(topCard); - } - controller.moveCardsToExile(topCard, source, game, true, source.getSourceId(), sourceObject.getIdName()); - } - } - } - } - - // cast the possible cards without paying the mana - Cards cardsToCast = new CardsImpl(); - cardsToCast.addAll(currentExiledCards); - boolean alreadyCast = false; - while (controller.canRespond() && !cardsToCast.isEmpty()) { - if (!controller.chooseUse(Outcome.PlayForFree, "Cast a" - + (alreadyCast ? "nother" : "") + " card exiled with " - + sourceObject.getLogName() + " without paying its mana cost?", source, game)) { - break; - } - - TargetCard targetCard = new TargetCard(1, Zone.EXILED, new FilterCard("nonland card to cast for free")); - if (!controller.choose(Outcome.PlayForFree, cardsToCast, targetCard, game)) { - break; - } - - alreadyCast = true; - Card card = game.getCard(targetCard.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - if (!cardWasCast) { - game.informPlayer(controller, "You're not able to cast " - + card.getIdName() + " or you canceled the casting."); - } - cardsToCast.remove(card); - } - } - return true; + if (controller == null) { + return false; } - return false; + // move cards from library to exile + Cards cards = new CardsImpl(); + for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { + Player player = game.getPlayer(playerId); + if (player != null) { + cards.add(player.getLibrary().getFromTop(game)); + } + } + controller.moveCards(cards, Zone.EXILED, source, game); + CardUtil.castMultipleWithAttributeForFree(controller, source, game, cards, filter); + return true; } } diff --git a/Mage.Sets/src/mage/cards/e/ExtractBrain.java b/Mage.Sets/src/mage/cards/e/ExtractBrain.java new file mode 100644 index 00000000000..b216be93128 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/ExtractBrain.java @@ -0,0 +1,77 @@ +package mage.cards.e; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +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.filter.StaticFilters; +import mage.game.Game; +import mage.players.Player; +import mage.target.common.TargetCardInHand; +import mage.target.common.TargetOpponent; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class ExtractBrain extends CardImpl { + + public ExtractBrain(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{U}{B}"); + + // Target opponent chooses X cards from their hand. Look at those cards. You may cast a spell from among them without paying its mana cost. + this.getSpellAbility().addEffect(new ExtractBrainEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); + } + + private ExtractBrain(final ExtractBrain card) { + super(card); + } + + @Override + public ExtractBrain copy() { + return new ExtractBrain(this); + } +} + +class ExtractBrainEffect extends OneShotEffect { + + ExtractBrainEffect() { + super(Outcome.Benefit); + staticText = "target opponent chooses X cards from their hand. Look at those cards. " + + "You may cast a spell from among them without paying its mana cost"; + } + + private ExtractBrainEffect(final ExtractBrainEffect effect) { + super(effect); + } + + @Override + public ExtractBrainEffect copy() { + return new ExtractBrainEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(source.getFirstTarget()); + int xValue = source.getManaCostsToPay().getX(); + if (controller == null || opponent == null || opponent.getHand().isEmpty() || xValue < 1) { + return false; + } + TargetCardInHand target = new TargetCardInHand( + Math.min(opponent.getHand().size(), xValue), StaticFilters.FILTER_CARD + ); + opponent.choose(Outcome.Detriment, opponent.getHand(), target, game); + Cards cards = new CardsImpl(target.getTargets()); + controller.lookAtCards(source, null, cards, game); + CardUtil.castSpellWithAttributesForFree(controller, source, game, cards, StaticFilters.FILTER_CARD); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java b/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java index 40308e5297b..06383a0b005 100644 --- a/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java +++ b/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java @@ -79,14 +79,10 @@ class FeatherTheRedeemedTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - if (!event.getPlayerId().equals(this.getControllerId())) { + if (!isControlledBy(event.getPlayerId())) { return false; } Spell spell = game.getStack().getSpell(event.getTargetId()); - return checkSpell(spell, game); - } - - private boolean checkSpell(Spell spell, Game game) { if (spell == null) { return false; } @@ -166,15 +162,15 @@ class FeatherTheRedeemedEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { ZoneChangeEvent zEvent = ((ZoneChangeEvent) event); - if (zEvent.getFromZone() == Zone.STACK - && zEvent.getToZone() == Zone.GRAVEYARD - && event.getSourceId() != null) { - if (event.getSourceId().equals(event.getTargetId()) && mor.getZoneChangeCounter() == game.getState().getZoneChangeCounter(event.getSourceId())) { - Spell spell = game.getStack().getSpell(mor.getSourceId()); - return spell != null && spell.isInstantOrSorcery(game); - } + if (zEvent.getFromZone() != Zone.STACK + || zEvent.getToZone() != Zone.GRAVEYARD + || event.getSourceId() == null + || !event.getSourceId().equals(event.getTargetId()) + || mor.getZoneChangeCounter() != game.getState().getZoneChangeCounter(event.getSourceId())) { + return false; } - return false; + Spell spell = game.getStack().getSpell(mor.getSourceId()); + return spell != null && spell.isInstantOrSorcery(game); } @Override diff --git a/Mage.Sets/src/mage/cards/f/FeveredSuspicion.java b/Mage.Sets/src/mage/cards/f/FeveredSuspicion.java new file mode 100644 index 00000000000..9032c3b34c9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/f/FeveredSuspicion.java @@ -0,0 +1,88 @@ +package mage.cards.f; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.keyword.ReboundAbility; +import mage.cards.*; +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.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class FeveredSuspicion extends CardImpl { + + public FeveredSuspicion(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{B}{R}"); + + // Each opponent exiles cards from the top of their library until they exile a nonland card. You may cast any number of spells from among those nonland cards without paying their mana costs. + this.getSpellAbility().addEffect(new FeveredSuspicionEffect()); + + // Rebound + this.addAbility(new ReboundAbility()); + } + + private FeveredSuspicion(final FeveredSuspicion card) { + super(card); + } + + @Override + public FeveredSuspicion copy() { + return new FeveredSuspicion(this); + } +} + +class FeveredSuspicionEffect extends OneShotEffect { + + FeveredSuspicionEffect() { + super(Outcome.Benefit); + staticText = "each opponent exiles cards from the top of their library until they exile a nonland card. " + + "You may cast any number of spells from among those nonland cards without paying their mana costs"; + } + + private FeveredSuspicionEffect(final FeveredSuspicionEffect effect) { + super(effect); + } + + @Override + public FeveredSuspicionEffect copy() { + return new FeveredSuspicionEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + Cards cards = new CardsImpl(); + Cards nonlands = new CardsImpl(); + for (UUID opponentId : game.getOpponents(source.getControllerId())) { + Player opponent = game.getPlayer(opponentId); + if (opponent == null) { + continue; + } + for (Card card : opponent.getLibrary().getCards(game)) { + cards.add(card); + if (!card.isLand(game)) { + nonlands.add(card); + break; + } + } + } + controller.moveCards(cards, Zone.EXILED, source, game); + nonlands.retainZone(Zone.EXILED, game); + CardUtil.castMultipleWithAttributeForFree( + controller, source, game, nonlands, + StaticFilters.FILTER_CARD + ); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/g/GeodeGolem.java b/Mage.Sets/src/mage/cards/g/GeodeGolem.java index bdc737f1a8b..6f547c790fa 100644 --- a/Mage.Sets/src/mage/cards/g/GeodeGolem.java +++ b/Mage.Sets/src/mage/cards/g/GeodeGolem.java @@ -1,22 +1,23 @@ package mage.cards.g; -import mage.ApprovingObject; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.TrampleAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.Cards; import mage.cards.CardsImpl; -import mage.constants.*; -import mage.filter.FilterCard; +import mage.constants.CardType; +import mage.constants.CommanderCardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; +import mage.util.CardUtil; -import java.util.Set; import java.util.UUID; /** @@ -35,7 +36,7 @@ public final class GeodeGolem extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever Geode Golem deals combat damage to a player, you may cast your commander from the command zone without paying its mana cost. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new GeodeGolemEffect(), true)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new GeodeGolemEffect(), false)); } private GeodeGolem(final GeodeGolem card) { @@ -63,48 +64,13 @@ class GeodeGolemEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Card selectedCommander = null; - - Set commandersInCommandZone = game.getCommanderCardsFromCommandZone(controller, CommanderCardType.COMMANDER_OR_OATHBREAKER); - if (commandersInCommandZone.isEmpty()) { - return false; - } - - // select from commanders - if (commandersInCommandZone.size() == 1) { - selectedCommander = commandersInCommandZone.stream().findFirst().get(); - } else { - TargetCard target = new TargetCard(Zone.COMMAND, new FilterCard("commander to cast without mana cost")); - target.setNotTarget(true); - if (controller.canRespond() - && controller.choose(Outcome.PlayForFree, new CardsImpl(commandersInCommandZone), target, game)) { - selectedCommander = commandersInCommandZone.stream() - .filter(c -> c.getId().equals(target.getFirstTarget())) - .findFirst() - .orElse(null); - - } - } - - if (selectedCommander == null) { - return false; - } - - // commander tax applies as additional cost - if (selectedCommander.getSpellAbility() != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), Boolean.TRUE); - Boolean commanderWasCast = controller.cast(controller.chooseAbilityForCast(selectedCommander, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + selectedCommander.getId(), null); - return commanderWasCast; - } else { - // play commander as land is xmage feature, but mtg rules for text "cast commander" doesn't allow that - // TODO: improve lands support for "cast your commander" (allow land play from mdf cards)? - return controller.playLand(selectedCommander, game, true); - } + if (controller == null) { + return false; } - return false; + Cards cards = new CardsImpl(game.getCommanderCardsFromCommandZone( + controller, CommanderCardType.COMMANDER_OR_OATHBREAKER + )); + return CardUtil.castSpellWithAttributesForFree(controller, source, game, cards, StaticFilters.FILTER_CARD); } @Override diff --git a/Mage.Sets/src/mage/cards/h/HazoretsUndyingFury.java b/Mage.Sets/src/mage/cards/h/HazoretsUndyingFury.java index 8a4fe475afc..99614eb7291 100644 --- a/Mage.Sets/src/mage/cards/h/HazoretsUndyingFury.java +++ b/Mage.Sets/src/mage/cards/h/HazoretsUndyingFury.java @@ -1,41 +1,44 @@ package mage.cards.h; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DontUntapInControllersUntapStepAllEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.StaticFilters; import mage.filter.common.FilterControlledLandPermanent; -import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; +import mage.util.CardUtil; import java.util.UUID; -import mage.ApprovingObject; /** * @author ciaccona007 */ public final class HazoretsUndyingFury extends CardImpl { + private static final FilterPermanent filter = new FilterControlledLandPermanent("Lands you control"); + public HazoretsUndyingFury(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}{R}"); //Shuffle your library, then exile the top four cards. //You may cast any number of nonland cards with converted mana //cost 5 or less from among them without paying their mana costs. - getSpellAbility().addEffect(new HazoretsUndyingFuryEffect()); + this.getSpellAbility().addEffect(new HazoretsUndyingFuryEffect()); //Land you control don't untap during your next untap step. this.getSpellAbility().addEffect(new DontUntapInControllersUntapStepAllEffect( Duration.UntilYourNextTurn, TargetController.YOU, - new FilterControlledLandPermanent("Lands you control")) - .setText("Lands you control don't untap during your next untap phase")); + StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND + ).setText("Lands you control don't untap during your next untap phase")); } private HazoretsUndyingFury(final HazoretsUndyingFury card) { @@ -50,19 +53,17 @@ public final class HazoretsUndyingFury extends CardImpl { class HazoretsUndyingFuryEffect extends OneShotEffect { - private static final FilterCard filter = new FilterCard( - "nonland cards with mana value 5 or less"); + private static final FilterCard filter = new FilterCard(); static { - filter.add(Predicates.not(CardType.LAND.getPredicate())); filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 6)); } public HazoretsUndyingFuryEffect() { super(Outcome.PlayForFree); - this.staticText = "Shuffle your library, then exile the top four cards. " - + "You may cast any number of nonland cards with mana value " - + "5 or less from among them without paying their mana costs"; + this.staticText = "Shuffle your library, then exile the top four cards. " + + "You may cast any number of spells with mana value " + + "5 or less from among them without paying their mana costs"; } public HazoretsUndyingFuryEffect(final HazoretsUndyingFuryEffect effect) { @@ -77,46 +78,15 @@ class HazoretsUndyingFuryEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - if (controller != null - && sourceObject != null) { - controller.shuffleLibrary(source, game); - // move cards from library to exile - controller.moveCardsToExile(controller.getLibrary().getTopCards(game, 4), - source, game, true, source.getSourceId(), sourceObject.getIdName()); - // cast the possible cards without paying the mana - ExileZone hazoretsUndyingFuryExileZone = game.getExile().getExileZone(source.getSourceId()); - Cards cardsToCast = new CardsImpl(); - if (hazoretsUndyingFuryExileZone == null) { - return true; - } - cardsToCast.addAll(hazoretsUndyingFuryExileZone.getCards(filter, - source.getSourceId(), source.getControllerId(), game)); - while (controller.canRespond() && !cardsToCast.isEmpty()) { - if (!controller.chooseUse(Outcome.PlayForFree, - "Cast (another) a card exiled with " - + sourceObject.getLogName() + " without paying its mana cost?", source, game)) { - break; - } - TargetCard targetCard = new TargetCard(1, Zone.EXILED, - new FilterCard("nonland card to cast for free")); - if (controller.choose(Outcome.PlayForFree, cardsToCast, targetCard, game)) { - Card card = game.getCard(targetCard.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - cardsToCast.remove(card); - if (!cardWasCast) { - game.informPlayer(controller, "You're not able to cast " - + card.getIdName() + " or you canceled the casting."); - } - } - } - } - return true; + if (controller == null) { + return false; } - return false; + controller.shuffleLibrary(source, game); + // move cards from library to exile + Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 4)); + controller.moveCards(cards, Zone.EXILED, source, game); + // cast the possible cards without paying the mana + CardUtil.castMultipleWithAttributeForFree(controller, source, game, cards, filter); + return true; } } diff --git a/Mage.Sets/src/mage/cards/h/HellcarverDemon.java b/Mage.Sets/src/mage/cards/h/HellcarverDemon.java index e3f7d05bcae..696695154dd 100644 --- a/Mage.Sets/src/mage/cards/h/HellcarverDemon.java +++ b/Mage.Sets/src/mage/cards/h/HellcarverDemon.java @@ -1,26 +1,24 @@ package mage.cards.h; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterNonlandCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.TargetCard; +import mage.util.CardUtil; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; import java.util.UUID; -import mage.ApprovingObject; /** * @author jeffwadsworth & L_J @@ -58,7 +56,7 @@ class HellcarverDemonEffect extends OneShotEffect { super(Outcome.PlayForFree); staticText = "sacrifice all other permanents you control and discard your hand. " + "Exile the top six cards of your library. You may cast any number of " - + "nonland cards exiled this way without paying their mana costs."; + + "spells from among cards exiled this way without paying their mana costs"; } public HellcarverDemonEffect(final HellcarverDemonEffect effect) { @@ -68,54 +66,25 @@ class HellcarverDemonEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller != null && sourceObject != null) { - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(source.getControllerId())) { - if (!Objects.equals(permanent, sourceObject)) { - permanent.sacrifice(source, game); - } - } - if (!controller.getHand().isEmpty()) { - int cardsInHand = controller.getHand().size(); - controller.discard(cardsInHand, false, false, source, game); - } - // move cards from library to exile - Set currentExiledCards = new HashSet<>(); - currentExiledCards.addAll(controller.getLibrary().getTopCards(game, 6)); - controller.moveCardsToExile(currentExiledCards, source, game, true, - source.getSourceId(), sourceObject.getIdName()); - - // cast the possible cards without paying the mana - Cards cardsToCast = new CardsImpl(); - cardsToCast.addAll(currentExiledCards); - boolean alreadyCast = false; - while (controller.canRespond() && !cardsToCast.isEmpty()) { - if (!controller.chooseUse(outcome, "Cast a" + (alreadyCast ? "another" : "") - + " card exiled with " + sourceObject.getLogName() - + " without paying its mana cost?", source, game)) { - break; - } - TargetCard targetCard = new TargetCard(1, Zone.EXILED, - new FilterNonlandCard("nonland card to cast for free")); - if (controller.choose(Outcome.PlayForFree, cardsToCast, targetCard, game)) { - alreadyCast = true; - Card card = game.getCard(targetCard.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - cardsToCast.remove(card); - if (!cardWasCast) { - game.informPlayer(controller, "You're not able to cast " - + card.getIdName() + " or you canceled the casting."); - } - } - } - } - return true; + if (controller == null) { + return false; } - return false; + MageObjectReference sourceMor = new MageObjectReference(source); + for (Permanent permanent : game.getBattlefield().getActivePermanents( + StaticFilters.FILTER_CONTROLLED_PERMANENT, + source.getControllerId(), source.getSourceId(), game + )) { + if (!sourceMor.refersTo(permanent, game)) { + permanent.sacrifice(source, game); + } + } + controller.discard(controller.getHand(), false, source, game); + CardUtil.castMultipleWithAttributeForFree( + controller, source, game, new CardsImpl( + controller.getLibrary().getTopCards(game, 6) + ), StaticFilters.FILTER_CARD + ); + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java b/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java index 66b78be6045..178d92eb694 100644 --- a/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java +++ b/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java @@ -1,25 +1,28 @@ package mage.cards.i; -import java.util.UUID; - -import mage.ApprovingObject; +import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; -import mage.filter.common.FilterPermanentCard; -import mage.filter.predicate.Predicates; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.UUID; /** - * * @author weirddan455 */ public final class InSearchOfGreatness extends CardImpl { @@ -30,8 +33,8 @@ public final class InSearchOfGreatness extends CardImpl { // At the beginning of your upkeep, you may cast a permanent spell from your hand with converted mana cost // equal to 1 plus the highest converted mana cost among other permanents you control // without paying its mana cost. If you don't, scry 1. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new InSearchOfGreatnessEffect(), - TargetController.YOU, false, false + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new InSearchOfGreatnessEffect(), TargetController.YOU, false )); } @@ -69,35 +72,19 @@ class InSearchOfGreatnessEffect extends OneShotEffect { if (controller == null) { return false; } - int cmc = 0; - UUID permId = source.getSourceId(); - for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controller.getId())) { - if (permanent != null && !permanent.getId().equals(permId)) { - int permCmc = permanent.getManaValue(); - if (permCmc > cmc) { - cmc = permCmc; - } - } - } - if (controller.chooseUse(outcome, "Cast a permanent spell from your hand with CMC equal to " - + ++cmc + "?", source, game)) { - FilterPermanentCard filter = new FilterPermanentCard("permanent spell from your hand"); - filter.add(Predicates.not(CardType.LAND.getPredicate())); - filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, cmc)); - TargetCardInHand target = new TargetCardInHand(filter); - if (controller.chooseTarget(outcome, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - if (cardWasCast) { - return true; - } - } - } - } - return controller.scry(1, source, game); + MageObjectReference sourceRef = new MageObjectReference(source); + int manaValue = game + .getBattlefield() + .getActivePermanents(StaticFilters.FILTER_CONTROLLED_PERMANENT, controller.getId(), game) + .stream() + .filter(Objects::nonNull) + .filter(permanent -> !sourceRef.refersTo(permanent, game)) + .mapToInt(MageObject::getManaValue) + .sum(); + FilterCard filter = new FilterCard(); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, manaValue + 1)); + return CardUtil.castSpellWithAttributesForFree( + controller, source, game, new CardsImpl(controller.getHand()), filter + ) || controller.scry(1, source, game); } } diff --git a/Mage.Sets/src/mage/cards/i/InvokeCalamity.java b/Mage.Sets/src/mage/cards/i/InvokeCalamity.java new file mode 100644 index 00000000000..fa16d546995 --- /dev/null +++ b/Mage.Sets/src/mage/cards/i/InvokeCalamity.java @@ -0,0 +1,135 @@ +package mage.cards.i; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.ExileSpellEffect; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class InvokeCalamity extends CardImpl { + + public InvokeCalamity(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{R}{R}{R}{R}"); + + // You may cast up to two instant and/or sorcery spells with total mana value 6 or less from your graveyard and/or hand without paying their mana costs. If those spells would be put into your graveyard, exile them instead. Exile Invoke Calamity. + this.getSpellAbility().addEffect(new InvokeCalamityEffect()); + this.getSpellAbility().addEffect(new ExileSpellEffect()); + } + + private InvokeCalamity(final InvokeCalamity card) { + super(card); + } + + @Override + public InvokeCalamity copy() { + return new InvokeCalamity(this); + } +} + +class InvokeCalamityEffect extends OneShotEffect { + + InvokeCalamityEffect() { + super(Outcome.Benefit); + staticText = "you may cast up to two instant and/or sorcery spells " + + "with total mana value 6 or less from your graveyard and/or hand without paying their mana costs. " + + "If those spells would be put into your graveyard, exile them instead"; + } + + private InvokeCalamityEffect(final InvokeCalamityEffect effect) { + super(effect); + } + + @Override + public InvokeCalamityEffect copy() { + return new InvokeCalamityEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getHand()); + cards.addAll(player.getGraveyard()); + CardUtil.castMultipleWithAttributeForFree( + player, source, game, cards, + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, + 2, new InvokeCalamityTracker() + ); + return true; + } +} + +class InvokeCalamityTracker implements CardUtil.SpellCastTracker { + + private int totalManaValue = 0; + + @Override + public boolean checkCard(Card card, Game game) { + return card.getManaValue() + totalManaValue <= 6; + } + + @Override + public void addCard(Card card, Ability source, Game game) { + totalManaValue += card.getManaValue(); + game.addEffect(new InvokeCalamityReplacementEffect(card, game), source); + } +} + +class InvokeCalamityReplacementEffect extends ReplacementEffectImpl { + + private final MageObjectReference mor; + + InvokeCalamityReplacementEffect(Card card, Game game) { + super(Duration.EndOfTurn, Outcome.Exile); + this.mor = new MageObjectReference(card.getMainCard(), game); + } + + private InvokeCalamityReplacementEffect(final InvokeCalamityReplacementEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public InvokeCalamityReplacementEffect copy() { + return new InvokeCalamityReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = mor.getCard(game); + return controller != null + && card != null + && controller.moveCards(card, Zone.EXILED, source, game); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + return zEvent.getToZone() == Zone.GRAVEYARD + && zEvent.getTargetId().equals(mor.getSourceId()); + } +} diff --git a/Mage.Sets/src/mage/cards/i/IzzetChemister.java b/Mage.Sets/src/mage/cards/i/IzzetChemister.java index eaf33025caf..82df45de737 100644 --- a/Mage.Sets/src/mage/cards/i/IzzetChemister.java +++ b/Mage.Sets/src/mage/cards/i/IzzetChemister.java @@ -9,37 +9,26 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.keyword.HasteAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; -import mage.target.common.TargetCardInExile; +import mage.target.common.TargetCardInGraveyard; +import mage.util.CardUtil; import java.util.UUID; -import mage.ApprovingObject; /** * @author TheElk801 */ public final class IzzetChemister extends CardImpl { - private static final FilterCard filter = new FilterOwnedCard("instant or sorcery card from your graveyard"); - - static { - filter.add(Predicates.or( - CardType.INSTANT.getPredicate(), - CardType.SORCERY.getPredicate()) - ); - } - public IzzetChemister(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}"); @@ -51,15 +40,16 @@ public final class IzzetChemister extends CardImpl { // Haste this.addAbility(HasteAbility.getInstance()); - // R, T: Exile target instant or sorcery card from your graveyard. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(this.getId(), this.getIdName()), new ManaCostsImpl("{R}")); + // {R}, {T}: Exile target instant or sorcery card from your graveyard. + Ability ability = new SimpleActivatedAbility( + new ExileTargetEffect().setToSourceExileZone(true), new ManaCostsImpl<>("{R}") + ); ability.addCost(new TapSourceCost()); - ability.addTarget(new TargetCard(Zone.GRAVEYARD, filter)); + ability.addTarget(new TargetCardInGraveyard(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY_FROM_YOUR_GRAVEYARD)); this.addAbility(ability); - // 1R, T: Sacrifice Izzet Chemister: Cast any number of cards exiled with Izzet Chemister without paying their mana costs. - IzzetChemisterCastFromExileEffect returnFromExileEffect = new IzzetChemisterCastFromExileEffect(this.getId(), "Cast any number of cards exiled with {this} without paying their mana costs."); - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, returnFromExileEffect, new ManaCostsImpl("{1}{R}")); + // {1}{R}, {T}: Sacrifice Izzet Chemister: Cast any number of cards exiled with Izzet Chemister without paying their mana costs. + ability = new SimpleActivatedAbility(new IzzetChemisterCastFromExileEffect(), new ManaCostsImpl<>("{1}{R}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); @@ -77,17 +67,13 @@ public final class IzzetChemister extends CardImpl { class IzzetChemisterCastFromExileEffect extends OneShotEffect { - private final UUID exileId; - - public IzzetChemisterCastFromExileEffect(UUID exileId, String description) { + public IzzetChemisterCastFromExileEffect() { super(Outcome.PlayForFree); - this.exileId = exileId; - this.setText(description); + staticText = "cast any number of cards exiled with {this} without paying their mana costs"; } public IzzetChemisterCastFromExileEffect(final IzzetChemisterCastFromExileEffect effect) { super(effect); - this.exileId = effect.exileId; } @Override @@ -97,38 +83,15 @@ class IzzetChemisterCastFromExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - ExileZone exile = game.getExile().getExileZone(exileId); Player controller = game.getPlayer(source.getControllerId()); - FilterCard filter = new FilterCard(); - if (controller != null - && exile != null) { - Cards cardsToExile = new CardsImpl(); - cardsToExile.addAll(exile.getCards(game)); - OuterLoop: - while (cardsToExile.count(filter, game) > 0) { - if (!controller.canRespond()) { - return false; - } - TargetCardInExile target = new TargetCardInExile(0, 1, filter, exileId, false); - target.setNotTarget(true); - while (controller.canRespond() - && cardsToExile.count(filter, game) > 0 - && controller.choose(Outcome.PlayForFree, cardsToExile, target, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(card, game, true), game, true, - new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - cardsToExile.remove(card); - } else { - break OuterLoop; - } - target.clearChosen(); - } - } - return true; + ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (controller == null || exile == null || exile.isEmpty()) { + return false; } - return false; + CardUtil.castMultipleWithAttributeForFree( + controller, source, game, new CardsImpl(exile), + StaticFilters.FILTER_CARD + ); + return true; } } diff --git a/Mage.Sets/src/mage/cards/j/JacesMindseeker.java b/Mage.Sets/src/mage/cards/j/JacesMindseeker.java index 9832c9717d5..ca4873ebab2 100644 --- a/Mage.Sets/src/mage/cards/j/JacesMindseeker.java +++ b/Mage.Sets/src/mage/cards/j/JacesMindseeker.java @@ -5,21 +5,20 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.common.FilterInstantOrSorceryCard; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; import mage.target.common.TargetOpponent; +import mage.util.CardUtil; -import java.util.Set; import java.util.UUID; -import mage.ApprovingObject; /** * @author LevelX2 @@ -75,44 +74,15 @@ class JaceMindseekerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Cards cardsToCast = new CardsImpl(); - Player targetOpponent = game.getPlayer(targetPointer.getFirst(game, source)); - if (targetOpponent != null) { - Set allCards = targetOpponent.millCards(5, source, game).getCards(game); - for (Card card : allCards) { - if (filter.match(card, game)) { - Zone zone = game.getState().getZone(card.getId()); - // If the five cards are put into a public zone such as exile instead - // of a graveyard (perhaps due to the ability of Rest in Peace), - // you can cast one of those instant or sorcery cards from that zone. - if (zone == Zone.GRAVEYARD - || zone == Zone.EXILED) { - cardsToCast.add(card); - } - } - } - - // cast an instant or sorcery for free - if (!cardsToCast.isEmpty()) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - TargetCard target = new TargetCard(Zone.GRAVEYARD, filter); // zone should be ignored here - target.setNotTarget(true); - if (controller.chooseUse(outcome, "Cast an instant or sorcery card from among them for free?", source, game) - && controller.choose(Outcome.PlayForFree, cardsToCast, target, game)) { - Card card = cardsToCast.get(target.getFirstTarget(), game); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - } - } - } - - } - return true; + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(source.getFirstTarget()); + if (controller == null || opponent == null) { + return false; } - return false; + return CardUtil.castSpellWithAttributesForFree( + controller, source, game, + opponent.millCards(5, source, game), + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY + ); } } diff --git a/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java b/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java index 72d48113490..2f3a3b78d9a 100644 --- a/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java +++ b/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java @@ -1,9 +1,5 @@ package mage.cards.j; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import mage.ApprovingObject; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -11,27 +7,25 @@ import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.WatcherScope; -import mage.filter.common.FilterInstantOrSorceryCard; -import mage.game.ExileZone; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; -import mage.target.common.TargetCardInExile; import mage.util.CardUtil; import mage.watchers.Watcher; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class JelevaNephaliasScourge extends CardImpl { @@ -112,8 +106,8 @@ class JelevaNephaliasCastEffect extends OneShotEffect { public JelevaNephaliasCastEffect() { super(Outcome.PlayForFree); - this.staticText = "you may cast an instant or sorcery card " - + "exiled with it without paying its mana cost"; + this.staticText = "you may cast an instant or sorcery spell " + + "from among cards exiled with {this} without paying its mana cost"; } public JelevaNephaliasCastEffect(final JelevaNephaliasCastEffect effect) { @@ -128,29 +122,14 @@ class JelevaNephaliasCastEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - if (exileZone != null - && exileZone.count(new FilterInstantOrSorceryCard(), game) > 0) { - if (controller.chooseUse(outcome, "Cast an instant or sorcery card from " - + "exile without paying its mana cost?", source, game)) { - TargetCardInExile target = new TargetCardInExile( - new FilterInstantOrSorceryCard(), CardUtil.getCardExileZoneId(game, source)); - if (controller.choose(Outcome.PlayForFree, exileZone, target, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - return cardWasCast; - } - } - } - } - return true; + if (controller == null) { + return false; } - return false; + Cards cards = new CardsImpl(game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source))); + return CardUtil.castSpellWithAttributesForFree( + controller, source, game, cards, + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY + ); } } @@ -170,8 +149,8 @@ class JelevaNephaliasWatcher extends Watcher { for (StackObject stackObject : game.getStack()) { if (stackObject instanceof Spell) { Spell spell = (Spell) stackObject; - manaSpendToCast.putIfAbsent(spell.getSourceId().toString() - + spell.getCard().getZoneChangeCounter(game), + manaSpendToCast.putIfAbsent(spell.getSourceId().toString() + + spell.getCard().getZoneChangeCounter(game), spell.getSpellAbility().getManaCostsToPay().manaValue()); } } diff --git a/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java b/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java index 8217dae71e7..63db0d2d487 100644 --- a/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java +++ b/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java @@ -1,7 +1,5 @@ package mage.cards.k; -import java.util.UUID; -import mage.ApprovingObject; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; @@ -11,23 +9,21 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.SearchEffect; -import mage.cards.*; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.constants.Zone; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; -import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInLibrary; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class KahoMinamoHistorian extends CardImpl { @@ -109,8 +105,8 @@ class KahoMinamoHistorianCastEffect extends OneShotEffect { public KahoMinamoHistorianCastEffect() { super(Outcome.PlayForFree); - this.staticText = "you may cast a card with mana value X " - + "exiled with {this} without paying its mana cost"; + this.staticText = "you may cast a spell with mana value X " + + "from among cards exiled with {this} without paying its mana cost"; } public KahoMinamoHistorianCastEffect(final KahoMinamoHistorianCastEffect effect) { @@ -125,24 +121,12 @@ class KahoMinamoHistorianCastEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - FilterCard filter = new FilterCard(); - filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, source.getManaCostsToPay().getX())); - TargetCardInExile target = new TargetCardInExile(filter, CardUtil.getCardExileZoneId(game, source)); - Cards cards = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - if (cards != null - && !cards.isEmpty() - && controller.choose(Outcome.PlayForFree, cards, target, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - } - } - return true; + Cards cards = new CardsImpl(game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source))); + if (controller == null || cards.isEmpty()) { + return false; } - return false; + FilterCard filter = new FilterCard(); + filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, source.getManaCostsToPay().getX())); + return CardUtil.castSpellWithAttributesForFree(controller, source, game, cards, filter); } } diff --git a/Mage.Sets/src/mage/cards/k/KariZevsExpertise.java b/Mage.Sets/src/mage/cards/k/KariZevsExpertise.java index 2f2b75f23d8..a92b7571acf 100644 --- a/Mage.Sets/src/mage/cards/k/KariZevsExpertise.java +++ b/Mage.Sets/src/mage/cards/k/KariZevsExpertise.java @@ -1,32 +1,39 @@ package mage.cards.k; -import java.util.UUID; import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; -import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Duration; import mage.constants.SubType; +import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author fireshoes */ public final class KariZevsExpertise extends CardImpl { private static final FilterPermanent filter = new FilterPermanent("creature or Vehicle"); + private static final FilterCard filter2 = new FilterCard("a spell with mana value 2 or less"); static { - filter.add(Predicates.or(CardType.CREATURE.getPredicate(), - SubType.VEHICLE.getPredicate())); + filter.add(Predicates.or( + CardType.CREATURE.getPredicate(), + SubType.VEHICLE.getPredicate() + )); + filter2.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 3)); } public KariZevsExpertise(UUID ownerId, CardSetInfo setInfo) { @@ -36,10 +43,12 @@ public final class KariZevsExpertise extends CardImpl { this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addEffect(new GainControlTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addEffect(new UntapTargetEffect().setText("Untap it")); - this.getSpellAbility().addEffect(new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.EndOfTurn).setText("It gains haste until end of turn")); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect( + HasteAbility.getInstance(), Duration.EndOfTurn + ).setText("It gains haste until end of turn")); // You may cast a card with converted mana cost 2 or less from your hand without paying its mana cost. - this.getSpellAbility().addEffect(new CastWithoutPayingManaCostEffect(2).concatBy("
")); + this.getSpellAbility().addEffect(new CastFromHandForFreeEffect(filter2).concatBy("
")); } private KariZevsExpertise(final KariZevsExpertise card) { diff --git a/Mage.Sets/src/mage/cards/k/KenBurningBrawler.java b/Mage.Sets/src/mage/cards/k/KenBurningBrawler.java index bedfec5b32c..df26123ab9b 100644 --- a/Mage.Sets/src/mage/cards/k/KenBurningBrawler.java +++ b/Mage.Sets/src/mage/cards/k/KenBurningBrawler.java @@ -5,17 +5,19 @@ import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilitySourceEffect; -import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; import mage.abilities.keyword.FirstStrikeAbility; import mage.abilities.keyword.ProwessAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; import java.util.UUID; @@ -59,12 +61,6 @@ public final class KenBurningBrawler extends CardImpl { class KenBurningBrawlerEffect extends OneShotEffect { - private static final FilterCard filter = new FilterCard("sorcery spell"); - - static { - filter.add(CardType.SORCERY.getPredicate()); - } - KenBurningBrawlerEffect() { super(Outcome.Benefit); staticText = "you may cast a sorcery spell from your hand with mana value " + @@ -82,8 +78,13 @@ class KenBurningBrawlerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - return new CastWithoutPayingManaCostEffect( - StaticValue.get((Integer) getValue("damage")), filter - ).apply(game, source); + Player player = game.getPlayer(source.getControllerId()); + if (player == null || player.getHand().isEmpty()) { + return false; + } + FilterCard filter = new FilterCard(); + filter.add(CardType.SORCERY.getPredicate()); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 1 + (Integer) getValue("damage"))); + return CardUtil.castSpellWithAttributesForFree(player, source, game, new CardsImpl(player.getHand()), filter); } } diff --git a/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java b/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java index 2385d6d5961..e0cecd87b5e 100644 --- a/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java +++ b/Mage.Sets/src/mage/cards/m/MaelstromArchangel.java @@ -1,31 +1,24 @@ package mage.cards.m; -import java.util.UUID; -import mage.ApprovingObject; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.abilities.keyword.FlyingAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.FilterCard; -import mage.filter.common.FilterNonlandCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetCardInHand; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class MaelstromArchangel extends CardImpl { + private static final FilterCard filter = new FilterCard("a spell"); + public MaelstromArchangel(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{U}{B}{R}{G}"); this.subtype.add(SubType.ANGEL); @@ -37,8 +30,7 @@ public final class MaelstromArchangel extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever Maelstrom Archangel deals combat damage to a player, you may cast a nonland card from your hand without paying its mana cost. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new MaelstromArchangelCastEffect(), false)); - + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new CastFromHandForFreeEffect(filter), false)); } private MaelstromArchangel(final MaelstromArchangel card) { @@ -50,53 +42,3 @@ public final class MaelstromArchangel extends CardImpl { return new MaelstromArchangel(this); } } - -class MaelstromArchangelCastEffect extends OneShotEffect { - - private static final FilterCard filter = new FilterNonlandCard("spell from your hand"); - - public MaelstromArchangelCastEffect() { - super(Outcome.PlayForFree); - this.staticText = "you may cast a spell from your hand without paying its mana cost"; - } - - public MaelstromArchangelCastEffect(final MaelstromArchangelCastEffect effect) { - super(effect); - } - - @Override - public MaelstromArchangelCastEffect copy() { - return new MaelstromArchangelCastEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Target target = new TargetCardInHand(filter); - if (target.canChoose(source.getSourceId(), controller.getId(), game) - && controller.chooseUse(outcome, "Cast a spell from your hand without paying its mana cost?", source, game)) { - Card cardToCast = null; - boolean cancel = false; - while (controller.canRespond() && !cancel) { - if (controller.chooseTarget(outcome, target, source, game)) { - cardToCast = game.getCard(target.getFirstTarget()); - if (cardToCast != null && cardToCast.getSpellAbility().canChooseTarget(game, controller.getId())) { - cancel = true; - } - } else { - cancel = true; - } - } - if (cardToCast != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(cardToCast, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), null); - } - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MindclawShaman.java b/Mage.Sets/src/mage/cards/m/MindclawShaman.java index ad1cc1ac463..bed6d16c69e 100644 --- a/Mage.Sets/src/mage/cards/m/MindclawShaman.java +++ b/Mage.Sets/src/mage/cards/m/MindclawShaman.java @@ -1,29 +1,25 @@ package mage.cards.m; -import java.util.UUID; -import mage.ApprovingObject; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.predicate.Predicates; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; import mage.target.common.TargetOpponent; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class MindclawShaman extends CardImpl { @@ -55,14 +51,6 @@ public final class MindclawShaman extends CardImpl { class MindclawShamanEffect extends OneShotEffect { - private static final FilterCard filter = new FilterCard("instant or sorcery card"); - - static { - filter.add(Predicates.or( - CardType.INSTANT.getPredicate(), - CardType.SORCERY.getPredicate())); - } - public MindclawShamanEffect() { super(Outcome.PlayForFree); this.staticText = "target opponent reveals their hand. You may cast " @@ -80,34 +68,15 @@ class MindclawShamanEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player targetOpponent = game.getPlayer(source.getFirstTarget()); - MageObject sourceObject = source.getSourceObject(game); - if (targetOpponent != null && sourceObject != null) { - if (!targetOpponent.getHand().isEmpty()) { - targetOpponent.revealCards(sourceObject.getName(), targetOpponent.getHand(), game); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - TargetCard target = new TargetCard(Zone.HAND, filter); - target.setNotTarget(true); - if (controller.choose(Outcome.PlayForFree, targetOpponent.getHand(), target, game)) { - Card chosenCard = targetOpponent.getHand().get(target.getFirstTarget(), game); - if (chosenCard != null) { - if (controller.chooseUse(Outcome.PlayForFree, "Cast the chosen card without " - + "paying its mana cost?", source, game)) { - game.getState().setValue("PlayFromNotOwnHandZone" + chosenCard.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(chosenCard, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + chosenCard.getId(), null); - } else { - game.informPlayers(sourceObject.getLogName() + ": " - + controller.getLogName() + " canceled casting the card."); - } - } - } - return true; - } - } + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(source.getFirstTarget()); + if (controller == null || opponent == null) { + return false; } - return false; + opponent.revealCards(source, opponent.getHand(), game); + return CardUtil.castSpellWithAttributesForFree( + controller, source, game, new CardsImpl(opponent.getHand()), + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY + ); } } diff --git a/Mage.Sets/src/mage/cards/m/MindleechMass.java b/Mage.Sets/src/mage/cards/m/MindleechMass.java index ed612c25768..1bc43c87c37 100644 --- a/Mage.Sets/src/mage/cards/m/MindleechMass.java +++ b/Mage.Sets/src/mage/cards/m/MindleechMass.java @@ -1,28 +1,24 @@ package mage.cards.m; -import java.util.UUID; -import mage.ApprovingObject; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.TrampleAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterNonlandCard; +import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author jeffwadsworth */ public final class MindleechMass extends CardImpl { @@ -39,7 +35,9 @@ public final class MindleechMass extends CardImpl { // Whenever Mindleech Mass deals combat damage to a player, you may look at that // player's hand. If you do, you may cast a nonland card in it without paying that card's mana cost. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new MindleechMassEffect(), true, true)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility( + new MindleechMassEffect(), true, true + )); } private MindleechMass(final MindleechMass card) { @@ -56,8 +54,8 @@ class MindleechMassEffect extends OneShotEffect { public MindleechMassEffect() { super(Outcome.PlayForFree); - this.staticText = "you may look at that player's hand. If you do, " - + "you may cast a nonland card in it without paying that card's mana cost"; + this.staticText = "look at that player's hand. If you do, you " + + "may cast a spell from among those cards without paying its mana cost"; } public MindleechMassEffect(final MindleechMassEffect effect) { @@ -71,27 +69,15 @@ class MindleechMassEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); - if (opponent != null && controller != null) { - Cards cardsInHand = new CardsImpl(); - cardsInHand.addAll(opponent.getHand()); - opponent.revealCards("Opponents hand", cardsInHand, game); - if (!cardsInHand.isEmpty() - && !cardsInHand.getCards(new FilterNonlandCard(), game).isEmpty()) { - TargetCard target = new TargetCard(1, Zone.HAND, new FilterNonlandCard()); - if (controller.chooseTarget(Outcome.PlayForFree, cardsInHand, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - } - } - } - return true; + Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (controller == null || opponent == null) { + return false; } - return false; + controller.lookAtCards(opponent.getName(), opponent.getHand(), game); + return CardUtil.castSpellWithAttributesForFree( + controller, source, game, new CardsImpl(opponent.getHand()), + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY + ); } } diff --git a/Mage.Sets/src/mage/cards/m/MuseVortex.java b/Mage.Sets/src/mage/cards/m/MuseVortex.java index 7545bab7454..dbfd7a95930 100644 --- a/Mage.Sets/src/mage/cards/m/MuseVortex.java +++ b/Mage.Sets/src/mage/cards/m/MuseVortex.java @@ -1,19 +1,22 @@ package mage.cards.m; -import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; +import mage.filter.StaticFilters; import mage.filter.common.FilterInstantOrSorceryCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; -import mage.target.common.TargetCardInExile; +import mage.util.CardUtil; import java.util.UUID; @@ -60,36 +63,21 @@ class MuseVortexEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { + Player player = game.getPlayer(source.getControllerId()); + int xValue = source.getManaCostsToPay().getX(); + if (player == null || xValue < 1) { return false; } - int xValue = source.getManaCostsToPay().getX(); - Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, xValue)); - controller.moveCards(cards, Zone.EXILED, source, game); + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, xValue)); + player.moveCards(cards, Zone.EXILED, source, game); cards.retainZone(Zone.EXILED, game); - FilterCard filter = new FilterInstantOrSorceryCard("an instant or sorcery card with mana value " + xValue + " or less"); + FilterCard filter = new FilterInstantOrSorceryCard(); filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); - TargetCardInExile target = new TargetCardInExile(filter); - target.setNotTarget(true); - if (controller.choose(Outcome.Benefit, cards, target, game)) { - Card card = cards.get(target.getFirstTarget(), game); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - cards.remove(card); - if (cardWasCast) { - cards.remove(card); - } else { - game.informPlayer(controller, "You're not able to cast " - + card.getIdName() + " or you canceled the casting."); - } - controller.putCardsOnTopOfLibrary(cards, game, source, true); - return true; - } - } - return false; + CardUtil.castSpellWithAttributesForFree(player, source, game, cards, filter); + cards.retainZone(Zone.EXILED, game); + player.moveCards(cards.getCards(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game), Zone.HAND, source, game); + cards.retainZone(Zone.EXILED, game); + player.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; } } diff --git a/Mage.Sets/src/mage/cards/o/OmnispellAdept.java b/Mage.Sets/src/mage/cards/o/OmnispellAdept.java index d012b3739a7..a11cb39da4a 100644 --- a/Mage.Sets/src/mage/cards/o/OmnispellAdept.java +++ b/Mage.Sets/src/mage/cards/o/OmnispellAdept.java @@ -5,30 +5,23 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardIdPredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetCardInHand; import java.util.UUID; -import mage.ApprovingObject; /** * @author TheElk801 */ public final class OmnispellAdept extends CardImpl { + private static final FilterCard filter = new FilterInstantOrSorceryCard("an instant or sorcery spell"); + public OmnispellAdept(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}"); @@ -40,7 +33,7 @@ public final class OmnispellAdept extends CardImpl { // {2}{U}, {T}: You may cast an instant or sorcery card from your hand // without paying its mana cost. Ability ability = new SimpleActivatedAbility( - new OmnispellAdeptEffect(), new ManaCostsImpl("{2}{U}") + new CastFromHandForFreeEffect(filter), new ManaCostsImpl<>("{2}{U}") ); ability.addCost(new TapSourceCost()); this.addAbility(ability); @@ -55,65 +48,3 @@ public final class OmnispellAdept extends CardImpl { return new OmnispellAdept(this); } } - -class OmnispellAdeptEffect extends OneShotEffect { - - private static final FilterCard filter = new FilterInstantOrSorceryCard( - "instant or sorcery card from your hand"); - - public OmnispellAdeptEffect() { - super(Outcome.PlayForFree); - this.staticText = "you may cast an instant or sorcery card from your hand " - + "without paying its mana cost"; - } - - public OmnispellAdeptEffect(final OmnispellAdeptEffect effect) { - super(effect); - } - - @Override - public OmnispellAdeptEffect copy() { - return new OmnispellAdeptEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - FilterCard realFilter = filter.copy(); - Target target = new TargetCardInHand(realFilter); - // choose one card until it possible to cast - if (target.canChoose(source.getSourceId(), controller.getId(), game) - && controller.chooseUse(Outcome.PlayForFree, "Cast an instant or sorcery " - + "card from your hand without paying its mana cost?", source, game)) { - Card cardToCast; - while (controller.canRespond() - && controller.chooseTarget(Outcome.PlayForFree, target, source, game)) { - cardToCast = game.getCard(target.getFirstTarget()); - if (cardToCast == null) { - break; - } - realFilter.add(Predicates.not(new CardIdPredicate(cardToCast.getId()))); // remove card from choose dialog (infinite fix) - - if (!cardToCast.getSpellAbility().canChooseTarget(game, controller.getId())) { - continue; - } - - game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(cardToCast, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), null); - - if (cardWasCast) { - break; - } else { - game.informPlayer(controller, "You're not able to cast " - + cardToCast.getIdName() + " or you canceled the casting."); - } - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/cards/o/OracleOfBones.java b/Mage.Sets/src/mage/cards/o/OracleOfBones.java index e101d727b32..595d90c4739 100644 --- a/Mage.Sets/src/mage/cards/o/OracleOfBones.java +++ b/Mage.Sets/src/mage/cards/o/OracleOfBones.java @@ -1,31 +1,21 @@ package mage.cards.o; -import java.util.UUID; -import mage.ApprovingObject; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.condition.common.TributeNotPaidCondition; import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.TributeAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.filter.FilterCard; -import mage.filter.common.FilterInstantOrSorceryCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetCardInHand; +import mage.filter.StaticFilters; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class OracleOfBones extends CardImpl { @@ -44,10 +34,14 @@ public final class OracleOfBones extends CardImpl { this.addAbility(new TributeAbility(2)); // When Oracle of Bones enters the battlefield, if tribute wasn't paid, // you may cast an instant or sorcery card from your hand without paying its mana cost. - TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new OracleOfBonesCastEffect(), false); - this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, TributeNotPaidCondition.instance, - "When {this} enters the battlefield, if its tribute wasn't paid, " - + "you may cast an instant or sorcery card from your hand without paying its mana cost.")); + this.addAbility(new ConditionalInterveningIfTriggeredAbility( + new EntersBattlefieldTriggeredAbility(new CastFromHandForFreeEffect( + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY + ), false), + TributeNotPaidCondition.instance, "When {this} enters the battlefield, " + + "if its tribute wasn't paid, you may cast an instant or " + + "sorcery spell from your hand without paying its mana cost." + )); } private OracleOfBones(final OracleOfBones card) { @@ -59,58 +53,3 @@ public final class OracleOfBones extends CardImpl { return new OracleOfBones(this); } } - -class OracleOfBonesCastEffect extends OneShotEffect { - - private static final FilterCard filter = new FilterInstantOrSorceryCard( - "instant or sorcery card from your hand"); - - public OracleOfBonesCastEffect() { - super(Outcome.PlayForFree); - this.staticText = "you may cast an instant or sorcery card " - + "from your hand without paying its mana cost"; - } - - public OracleOfBonesCastEffect(final OracleOfBonesCastEffect effect) { - super(effect); - } - - @Override - public OracleOfBonesCastEffect copy() { - return new OracleOfBonesCastEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Target target = new TargetCardInHand(filter); - if (target.canChoose(source.getSourceId(), controller.getId(), game) - && controller.chooseUse(outcome, "Cast an instant or sorcery " - + "card from your hand without paying its mana cost?", source, game)) { - Card cardToCast = null; - boolean cancel = false; - while (controller.canRespond() - && !cancel) { - if (controller.chooseTarget(outcome, target, source, game)) { - cardToCast = game.getCard(target.getFirstTarget()); - if (cardToCast != null - && cardToCast.getSpellAbility().canChooseTarget(game, controller.getId())) { - cancel = true; - } - } else { - cancel = true; - } - } - if (cardToCast != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(cardToCast, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), null); - } - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/r/Reinterpret.java b/Mage.Sets/src/mage/cards/r/Reinterpret.java index c48522aab3f..d904f31f555 100644 --- a/Mage.Sets/src/mage/cards/r/Reinterpret.java +++ b/Mage.Sets/src/mage/cards/r/Reinterpret.java @@ -2,14 +2,18 @@ package mage.cards.r; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Outcome; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.stack.Spell; +import mage.players.Player; import mage.target.TargetSpell; +import mage.util.CardUtil; import java.util.UUID; @@ -56,12 +60,17 @@ class ReinterpretEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Spell spell = game.getSpell(source.getFirstTarget()); - if (spell == null) { + Player controller = game.getPlayer(source.getControllerId()); + if (spell == null || controller == null) { return false; } int manaValue = spell.getManaValue(); - game.getStack().counter(spell.getId(), source, game); - new CastWithoutPayingManaCostEffect(manaValue).apply(game, source); + spell.counter(source, game); + FilterCard filter = new FilterCard(); + filter.add(new ManaValuePredicate( + ComparisonType.FEWER_THAN, manaValue + 1 + )); + CardUtil.castSpellWithAttributesForFree(controller, source, game, controller.getHand(), filter); return true; } } diff --git a/Mage.Sets/src/mage/cards/r/RishkarsExpertise.java b/Mage.Sets/src/mage/cards/r/RishkarsExpertise.java index 1db510524f0..e2635e62af7 100644 --- a/Mage.Sets/src/mage/cards/r/RishkarsExpertise.java +++ b/Mage.Sets/src/mage/cards/r/RishkarsExpertise.java @@ -1,20 +1,29 @@ - package mage.cards.r; -import java.util.UUID; import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; + +import java.util.UUID; /** * @author fireshoes */ public final class RishkarsExpertise extends CardImpl { + private static final FilterCard filter = new FilterCard("a spell with mana value 5 or less"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 6)); + } + public RishkarsExpertise(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}{G}"); @@ -24,7 +33,7 @@ public final class RishkarsExpertise extends CardImpl { this.getSpellAbility().addEffect(effect); // You may cast a card with converted mana cost 5 or less from your hand without paying its mana cost. - this.getSpellAbility().addEffect(new CastWithoutPayingManaCostEffect(5).concatBy("
")); + this.getSpellAbility().addEffect(new CastFromHandForFreeEffect(filter).concatBy("
")); } private RishkarsExpertise(final RishkarsExpertise card) { diff --git a/Mage.Sets/src/mage/cards/r/RodOfAbsorption.java b/Mage.Sets/src/mage/cards/r/RodOfAbsorption.java new file mode 100644 index 00000000000..4da3c94390f --- /dev/null +++ b/Mage.Sets/src/mage/cards/r/RodOfAbsorption.java @@ -0,0 +1,200 @@ +package mage.cards.r; + +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.cards.*; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.game.stack.Spell; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class RodOfAbsorption extends CardImpl { + + public RodOfAbsorption(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}"); + + // Whenever a player casts an instant or sorcery spell, exile it instead of putting it into a graveyard as it resolves. + this.addAbility(new RodOfAbsorptionTriggeredAbility()); + + // {X}, {T}, Sacrifice Rod of Absorption: You may cast any number of spells from among cards exiled with Rod of Absorption with total mana value X or less without paying their mana costs. + Ability ability = new SimpleActivatedAbility(new RodOfAbsorptionCastEffect(), new ManaCostsImpl<>("{X}")); + ability.addCost(new TapSourceCost()); + ability.addCost(new SacrificeSourceCost()); + this.addAbility(ability); + } + + private RodOfAbsorption(final RodOfAbsorption card) { + super(card); + } + + @Override + public RodOfAbsorption copy() { + return new RodOfAbsorption(this); + } +} + +class RodOfAbsorptionTriggeredAbility extends TriggeredAbilityImpl { + + RodOfAbsorptionTriggeredAbility() { + super(Zone.BATTLEFIELD, null, false); + } + + private RodOfAbsorptionTriggeredAbility(final RodOfAbsorptionTriggeredAbility ability) { + super(ability); + } + + @Override + public RodOfAbsorptionTriggeredAbility copy() { + return new RodOfAbsorptionTriggeredAbility(this); + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.SPELL_CAST; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Spell spell = game.getStack().getSpell(event.getTargetId()); + if (spell == null || !spell.isInstantOrSorcery(game)) { + return false; + } + this.getEffects().clear(); + this.addEffect(new RodOfAbsorptionExileEffect(spell, game)); + return true; + } + + @Override + public String getRule() { + return "Whenever a player casts an instant or sorcery spell, " + + "exile it instead of putting it into a graveyard as it resolves."; + } +} + +class RodOfAbsorptionExileEffect extends ReplacementEffectImpl { + + private final MageObjectReference mor; + + RodOfAbsorptionExileEffect(Spell spell, Game game) { + super(Duration.WhileOnStack, Outcome.Benefit); + this.mor = new MageObjectReference(spell, game); + } + + private RodOfAbsorptionExileEffect(final RodOfAbsorptionExileEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Spell sourceSpell = game.getStack().getSpell(event.getTargetId()); + if (sourceSpell == null || sourceSpell.isCopy()) { + return false; + } + Player player = game.getPlayer(sourceSpell.getOwnerId()); + if (player == null) { + return false; + } + player.moveCardsToExile( + sourceSpell, source, game, false, + CardUtil.getExileZoneId(game, source), + CardUtil.getSourceName(game, source) + ); + return true; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zEvent = ((ZoneChangeEvent) event); + if (zEvent.getFromZone() != Zone.STACK + || zEvent.getToZone() != Zone.GRAVEYARD + || event.getSourceId() == null + || !event.getSourceId().equals(event.getTargetId()) + || mor.getZoneChangeCounter() != game.getState().getZoneChangeCounter(event.getSourceId())) { + return false; + } + Spell spell = game.getStack().getSpell(mor.getSourceId()); + return spell != null && spell.isInstantOrSorcery(game); + } + + @Override + public RodOfAbsorptionExileEffect copy() { + return new RodOfAbsorptionExileEffect(this); + } +} + +class RodOfAbsorptionCastEffect extends OneShotEffect { + + RodOfAbsorptionCastEffect() { + super(Outcome.Benefit); + staticText = "you may cast any number of spells from among cards exiled with " + + "{this} with total mana value X or less without paying their mana costs"; + } + + private RodOfAbsorptionCastEffect(final RodOfAbsorptionCastEffect effect) { + super(effect); + } + + @Override + public RodOfAbsorptionCastEffect copy() { + return new RodOfAbsorptionCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + Cards cards = new CardsImpl(game.getExile().getExileZone(CardUtil.getExileZoneId(game, source))); + if (player == null || cards.isEmpty()) { + return false; + } + CardUtil.castMultipleWithAttributeForFree( + player, source, game, cards, StaticFilters.FILTER_CARD, Integer.MAX_VALUE, + new RodOfAbsorptionTracker(source.getManaCostsToPay().getX()) + ); + return true; + } +} + +class RodOfAbsorptionTracker implements CardUtil.SpellCastTracker { + + private final int xValue; + private int totalManaValue = 0; + + RodOfAbsorptionTracker(int xValue) { + this.xValue = xValue; + } + + @Override + public boolean checkCard(Card card, Game game) { + return card.getManaValue() + totalManaValue <= xValue; + } + + @Override + public void addCard(Card card, Ability source, Game game) { + totalManaValue += card.getManaValue(); + } +} diff --git a/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java b/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java index 1eade2f0432..36b9ceda84c 100644 --- a/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java +++ b/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java @@ -1,22 +1,18 @@ package mage.cards.s; -import java.util.UUID; -import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SuperType; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.FilterSpell; +import mage.filter.StaticFilters; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.filter.predicate.Predicates; @@ -27,11 +23,11 @@ import mage.game.stack.StackObject; import mage.players.Player; import mage.target.Target; import mage.target.TargetSpell; -import mage.target.common.TargetCardInExile; import mage.util.CardUtil; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class ShellOfTheLastKappa extends CardImpl { @@ -50,16 +46,18 @@ public final class ShellOfTheLastKappa extends CardImpl { addSuperType(SuperType.LEGENDARY); // {3}, {tap}: Exile target instant or sorcery spell that targets you. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new ShellOfTheLastKappaEffect(), new ManaCostsImpl("{3}")); + Ability ability = new SimpleActivatedAbility( + new ShellOfTheLastKappaEffect(), new GenericManaCost(3) + ); ability.addCost(new TapSourceCost()); Target target = new TargetSpell(filter); ability.addTarget(target); this.addAbility(ability); // {3}, {tap}, Sacrifice Shell of the Last Kappa: You may cast a card // exiled with Shell of the Last Kappa without paying its mana cost. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new ShellOfTheLastKappaCastEffect(), new ManaCostsImpl("{3}")); + ability = new SimpleActivatedAbility( + new ShellOfTheLastKappaCastEffect(), new GenericManaCost(3) + ); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); this.addAbility(ability); @@ -118,7 +116,7 @@ class ShellOfTheLastKappaCastEffect extends OneShotEffect { public ShellOfTheLastKappaCastEffect() { super(Outcome.PlayForFree); - this.staticText = "You may cast a card exiled with {this} without paying its mana cost"; + this.staticText = "You may cast a spell from among cards exiled with {this} without paying its mana cost"; } public ShellOfTheLastKappaCastEffect(final ShellOfTheLastKappaCastEffect effect) { @@ -134,25 +132,11 @@ class ShellOfTheLastKappaCastEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller != null - && sourcePermanent != null) { - TargetCardInExile target = new TargetCardInExile(new FilterCard(), - CardUtil.getExileZoneId(game, source.getSourceId(), sourcePermanent.getZoneChangeCounter(game))); - if (controller.choose(Outcome.PlayForFree, game.getExile() - .getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), - sourcePermanent.getZoneChangeCounter(game))), target, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null - && controller.chooseUse(outcome, "Cast " + card.getLogName() + " without paying its mana cost?", source, game)) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - return cardWasCast; - } - } + if (controller == null || sourcePermanent == null) { + return false; } - return false; + Cards cards = new CardsImpl(game.getExile().getExileZone(CardUtil.getExileZoneId(game, source))); + return CardUtil.castSpellWithAttributesForFree(controller, source, game, cards, StaticFilters.FILTER_CARD); } } diff --git a/Mage.Sets/src/mage/cards/s/SilentBladeOni.java b/Mage.Sets/src/mage/cards/s/SilentBladeOni.java index a9a95eec106..b2ae3ca57ee 100644 --- a/Mage.Sets/src/mage/cards/s/SilentBladeOni.java +++ b/Mage.Sets/src/mage/cards/s/SilentBladeOni.java @@ -3,21 +3,20 @@ package mage.cards.s; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.NinjutsuAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; +import mage.util.CardUtil; import java.util.UUID; -import mage.ApprovingObject; /** * @author LevelX2 @@ -56,8 +55,8 @@ class SilentBladeOniEffect extends OneShotEffect { SilentBladeOniEffect() { super(Outcome.PlayForFree); - this.staticText = "look at that player's hand. " - + "You may cast a nonland card in it without paying that card's mana cost"; + this.staticText = "look at that player's hand. You may cast a spell " + + "from among those cards without paying its mana cost"; } private SilentBladeOniEffect(final SilentBladeOniEffect effect) { @@ -71,32 +70,15 @@ class SilentBladeOniEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); Player controller = game.getPlayer(source.getControllerId()); - if (opponent == null - || controller == null) { + Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); + if (controller == null || opponent == null) { return false; } - Cards cardsInHand = new CardsImpl(); - cardsInHand.addAll(opponent.getHand()); - if (cardsInHand.isEmpty()) { - return true; - } - TargetCard target = new TargetCard( - 0, 1, Zone.HAND, StaticFilters.FILTER_CARD_A_NON_LAND + controller.lookAtCards(opponent.getName(), opponent.getHand(), game); + return CardUtil.castSpellWithAttributesForFree( + controller, source, game, new CardsImpl(opponent.getHand()), + StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY ); - if (!controller.chooseUse(outcome, "Cast a card from " + opponent.getName() + "'s hand?", source, game) - || !controller.chooseTarget(outcome, cardsInHand, target, source, game)) { - return true; - } - Card card = game.getCard(target.getFirstTarget()); - if (card == null) { - return false; - } - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - return cardWasCast; } } diff --git a/Mage.Sets/src/mage/cards/s/SpectralArcanist.java b/Mage.Sets/src/mage/cards/s/SpectralArcanist.java new file mode 100644 index 00000000000..cbc7ee912fd --- /dev/null +++ b/Mage.Sets/src/mage/cards/s/SpectralArcanist.java @@ -0,0 +1,164 @@ +package mage.cards.s; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.hint.Hint; +import mage.abilities.hint.ValueHint; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.*; +import mage.constants.*; +import mage.filter.FilterCard; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterInstantOrSorceryCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.Objects; +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class SpectralArcanist extends CardImpl { + + public SpectralArcanist(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}"); + + this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.WIZARD); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // When Spectral Arcanist enters the battlefield, you may cast an instant or sorcery spell with mana value less than or equal to the number of Spirits you control from a graveyard without paying its mana cost. If that spell would be put into a graveyard, exile it instead. + this.addAbility(new EntersBattlefieldTriggeredAbility(new SpectralArcanistCastEffect()) + .addHint(SpectralArcanistCastEffect.getHint())); + } + + private SpectralArcanist(final SpectralArcanist card) { + super(card); + } + + @Override + public SpectralArcanist copy() { + return new SpectralArcanist(this); + } +} + +class SpectralArcanistCastEffect extends OneShotEffect { + + private static final FilterPermanent filter = new FilterControlledPermanent(SubType.SPIRIT); + private static final Hint hint = new ValueHint("Spirits you control", new PermanentsOnBattlefieldCount(filter)); + + SpectralArcanistCastEffect() { + super(Outcome.Benefit); + staticText = "you may cast an instant or sorcery spell with mana value less than or equal to " + + "the number of Spirits you control from a graveyard without paying its mana cost. " + + "If that spell would be put into a graveyard, exile it instead"; + } + + private SpectralArcanistCastEffect(final SpectralArcanistCastEffect effect) { + super(effect); + } + + @Override + public SpectralArcanistCastEffect copy() { + return new SpectralArcanistCastEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + FilterCard filterCard = new FilterInstantOrSorceryCard(); + filterCard.add(new ManaValuePredicate( + ComparisonType.FEWER_THAN, + game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game) + )); + Cards cards = new CardsImpl(); + game.getState() + .getPlayersInRange(source.getControllerId(), game) + .stream() + .map(game::getPlayer) + .filter(Objects::nonNull) + .map(Player::getGraveyard) + .forEach(cards::addAll); + return CardUtil.castSpellWithAttributesForFree( + player, source, game, cards, filterCard, + SpectralArcanistTracker.instance + ); + } + + public static Hint getHint() { + return hint; + } +} + +enum SpectralArcanistTracker implements CardUtil.SpellCastTracker { + instance; + + @Override + public boolean checkCard(Card card, Game game) { + return true; + } + + @Override + public void addCard(Card card, Ability source, Game game) { + game.addEffect(new SpectralArcanistReplacementEffect(card, game), source); + } +} + +class SpectralArcanistReplacementEffect extends ReplacementEffectImpl { + + private final MageObjectReference mor; + + SpectralArcanistReplacementEffect(Card card, Game game) { + super(Duration.EndOfTurn, Outcome.Exile); + this.mor = new MageObjectReference(card.getMainCard(), game); + } + + private SpectralArcanistReplacementEffect(final SpectralArcanistReplacementEffect effect) { + super(effect); + this.mor = effect.mor; + } + + @Override + public SpectralArcanistReplacementEffect copy() { + return new SpectralArcanistReplacementEffect(this); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = mor.getCard(game); + return controller != null + && card != null + && controller.moveCards(card, Zone.EXILED, source, game); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ZONE_CHANGE; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + return zEvent.getToZone() == Zone.GRAVEYARD + && zEvent.getTargetId().equals(mor.getSourceId()); + } +} diff --git a/Mage.Sets/src/mage/cards/s/SramsExpertise.java b/Mage.Sets/src/mage/cards/s/SramsExpertise.java index 6334c3bf86e..32332a5628a 100644 --- a/Mage.Sets/src/mage/cards/s/SramsExpertise.java +++ b/Mage.Sets/src/mage/cards/s/SramsExpertise.java @@ -1,29 +1,36 @@ - - package mage.cards.s; -import java.util.UUID; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.permanent.token.ServoToken; +import java.util.UUID; + /** * @author fireshoes */ public final class SramsExpertise extends CardImpl { + private static final FilterCard filter = new FilterCard("a spell with mana value 3 or less"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + } + public SramsExpertise(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{W}{W}"); - // Create three 1/1 colorless Servo artifact creature tokens. this.getSpellAbility().addEffect(new CreateTokenEffect(new ServoToken(), 3)); // You may cast a card with converted mana cost 3 or less from your hand without paying its mana cost. - this.getSpellAbility().addEffect(new CastWithoutPayingManaCostEffect(3).concatBy("
")); + this.getSpellAbility().addEffect(new CastFromHandForFreeEffect(filter).concatBy("
")); } private SramsExpertise(final SramsExpertise card) { diff --git a/Mage.Sets/src/mage/cards/s/SunbirdsInvocation.java b/Mage.Sets/src/mage/cards/s/SunbirdsInvocation.java index 83d6e0e0321..64bafb1026a 100644 --- a/Mage.Sets/src/mage/cards/s/SunbirdsInvocation.java +++ b/Mage.Sets/src/mage/cards/s/SunbirdsInvocation.java @@ -1,13 +1,9 @@ package mage.cards.s; -import java.util.UUID; -import mage.ApprovingObject; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.cards.Cards; @@ -17,17 +13,17 @@ import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.common.FilterNonlandCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.players.Player; -import mage.target.TargetCard; import mage.target.targetpointer.FixedTarget; +import mage.util.CardUtil; + +import java.util.UUID; /** - * * @author spjspj */ public final class SunbirdsInvocation extends CardImpl { @@ -54,11 +50,11 @@ public final class SunbirdsInvocation extends CardImpl { class SunbirdsInvocationTriggeredAbility extends SpellCastControllerTriggeredAbility { - public SunbirdsInvocationTriggeredAbility() { + SunbirdsInvocationTriggeredAbility() { super(new SunbirdsInvocationEffect(), false); } - public SunbirdsInvocationTriggeredAbility(SunbirdsInvocationTriggeredAbility ability) { + private SunbirdsInvocationTriggeredAbility(SunbirdsInvocationTriggeredAbility ability) { super(ability); } @@ -87,12 +83,10 @@ class SunbirdsInvocationTriggeredAbility extends SpellCastControllerTriggeredAbi @Override public String getRule() { - return "Whenever you cast a spell from your hand, " - + "reveal the top X cards of your library, " - + "where X is that spell's mana value. " - + "You may cast a card revealed this way with " - + "mana value X or less without paying its mana cost." - + " Put the rest on the bottom of your library in a random order."; + return "Whenever you cast a spell from your hand, reveal the top X cards of your library, " + + "where X is that spell's mana value. You may cast a spell with mana value X or less " + + "from among cards revealed this way without paying its mana cost. " + + "Put the rest on the bottom of your library in a random order."; } } @@ -100,10 +94,6 @@ class SunbirdsInvocationEffect extends OneShotEffect { public SunbirdsInvocationEffect() { super(Outcome.PutCardInPlay); - staticText = "reveal the top X cards of your library, where X is that " - + "spell's mana value. You may cast a card revealed this " - + "way with mana value X or less without paying its mana cost. " - + "Put the rest on the bottom of your library in a random order"; } public SunbirdsInvocationEffect(final SunbirdsInvocationEffect effect) { @@ -113,9 +103,7 @@ class SunbirdsInvocationEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); - if (controller == null - || sourceObject == null) { + if (controller == null) { return false; } Spell spell = game.getSpellOrLKIStack(this.getTargetPointer().getFirst(game, source)); @@ -124,29 +112,15 @@ class SunbirdsInvocationEffect extends OneShotEffect { } int xValue = spell.getManaValue(); Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, xValue)); - if (!cards.isEmpty()) { - controller.revealCards(sourceObject.getIdName(), cards, game); - - FilterCard filter = new FilterNonlandCard("card revealed this way with mana value " + xValue + " or less"); - filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); - TargetCard target = new TargetCard(1, Zone.LIBRARY, filter); - - if (controller.chooseTarget(Outcome.PlayForFree, cards, target, source, game)) { - Card card = cards.get(target.getFirstTarget(), game); - if (card != null) { - if (controller.chooseUse(Outcome.Benefit, "Cast " + card.getLogName() + " without paying its mana cost?", source, game)) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - if (cardWasCast) { - cards.remove(card); - } - } - } - } - controller.putCardsOnBottomOfLibrary(cards, game, source, false); + if (cards.isEmpty()) { + return true; } + controller.revealCards(source, cards, game); + FilterCard filter = new FilterCard(); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue + 1)); + CardUtil.castSpellWithAttributesForFree(controller, source, game, cards, filter); + cards.retainZone(Zone.LIBRARY, game); + controller.putCardsOnBottomOfLibrary(cards, game, source, false); return true; } diff --git a/Mage.Sets/src/mage/cards/s/SurtlandElementalist.java b/Mage.Sets/src/mage/cards/s/SurtlandElementalist.java index 5d8d9d2b309..2a9b862e848 100644 --- a/Mage.Sets/src/mage/cards/s/SurtlandElementalist.java +++ b/Mage.Sets/src/mage/cards/s/SurtlandElementalist.java @@ -1,34 +1,29 @@ package mage.cards.s; -import java.util.UUID; - -import mage.ApprovingObject; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.costs.OrCost; import mage.abilities.costs.common.RevealTargetFromHandCost; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.constants.Outcome; -import mage.constants.SubType; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.FilterCard; -import mage.filter.StaticFilters; -import mage.game.Game; -import mage.players.Player; +import mage.filter.common.FilterInstantOrSorceryCard; import mage.target.common.TargetCardInHand; +import java.util.UUID; + /** - * * @author weirddan455 */ public final class SurtlandElementalist extends CardImpl { private static final FilterCard filter = new FilterCard("a Giant card from your hand"); + private static final FilterCard filter2 = new FilterInstantOrSorceryCard("an instant or sorcery spell"); + static { filter.add(SubType.GIANT.getPredicate()); } @@ -48,7 +43,7 @@ public final class SurtlandElementalist extends CardImpl { "reveal a Giant card from your hand or pay {2}")); // Whenever Surtland Elementalist attacks, you may cast an instant or sorcery spell from your hand without paying its mana cost. - this.addAbility(new AttacksTriggeredAbility(new SurtlandElementalistEffect(), true)); + this.addAbility(new AttacksTriggeredAbility(new CastFromHandForFreeEffect(filter2), true)); } private SurtlandElementalist(final SurtlandElementalist card) { @@ -60,39 +55,3 @@ public final class SurtlandElementalist extends CardImpl { return new SurtlandElementalist(this); } } - -class SurtlandElementalistEffect extends OneShotEffect { - - public SurtlandElementalistEffect () { - super(Outcome.PlayForFree); - this.staticText = "cast an instant or sorcery spell from your hand without paying its mana cost"; - } - - private SurtlandElementalistEffect(final SurtlandElementalistEffect effect) { - super(effect); - } - - @Override - public SurtlandElementalistEffect copy() { - return new SurtlandElementalistEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - TargetCardInHand target = new TargetCardInHand(0, 1, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY); - if (player.chooseTarget(Outcome.PlayForFree, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - boolean cardWasCast = player.cast(player.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - return cardWasCast; - } - } - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SvellaIceShaper.java b/Mage.Sets/src/mage/cards/s/SvellaIceShaper.java index 8b7437056d1..703f974f971 100644 --- a/Mage.Sets/src/mage/cards/s/SvellaIceShaper.java +++ b/Mage.Sets/src/mage/cards/s/SvellaIceShaper.java @@ -1,6 +1,5 @@ package mage.cards.s; -import mage.ApprovingObject; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; @@ -9,19 +8,17 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; -import mage.cards.*; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.SubType; -import mage.constants.SuperType; -import mage.filter.common.FilterNonlandCard; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.*; +import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.token.IcyManalithToken; import mage.players.Player; -import mage.target.TargetCard; -import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; -import java.util.Set; import java.util.UUID; /** @@ -47,7 +44,7 @@ public final class SvellaIceShaper extends CardImpl { this.addAbility(ability); // {6}{R}{G}, {T}: Look at the top four cards of your library. You may cast a spell from among them without paying its mana cost. Put the rest on the bottom of your library in a random order. - ability = new SimpleActivatedAbility(new SvellaIceShaperEffect(), new ManaCostsImpl("{6}{R}{G}")); + ability = new SimpleActivatedAbility(new SvellaIceShaperEffect(), new ManaCostsImpl<>("{6}{R}{G}")); ability.addCost(new TapSourceCost()); this.addAbility(ability); } @@ -86,23 +83,9 @@ class SvellaIceShaperEffect extends OneShotEffect { if (controller == null) { return false; } - Set cardsSet = controller.getLibrary().getTopCards(game, 4); - Cards cards = new CardsImpl(cardsSet); - TargetCard target = new TargetCardInLibrary(0, 1, - new FilterNonlandCard("card to cast without paying its mana cost")); - controller.choose(Outcome.PlayForFree, cards, target, game); - Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); - if (card == null) { - controller.putCardsOnBottomOfLibrary(cards, game, source, false); - return true; - } - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - if (cardWasCast) { - cards.remove(card); - } + Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 4)); + CardUtil.castSpellWithAttributesForFree(controller, source, game, cards, StaticFilters.FILTER_CARD); + cards.retainZone(Zone.LIBRARY, game); controller.putCardsOnBottomOfLibrary(cards, game, source, false); return true; } diff --git a/Mage.Sets/src/mage/cards/t/TalentOfTheTelepath.java b/Mage.Sets/src/mage/cards/t/TalentOfTheTelepath.java index a8448d2ae13..94ede04f9e1 100644 --- a/Mage.Sets/src/mage/cards/t/TalentOfTheTelepath.java +++ b/Mage.Sets/src/mage/cards/t/TalentOfTheTelepath.java @@ -1,23 +1,24 @@ package mage.cards.t; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.condition.common.SpellMasteryCondition; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +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.StaticFilters; import mage.filter.common.FilterInstantOrSorceryCard; import mage.game.Game; import mage.players.Player; -import mage.target.TargetCard; import mage.target.common.TargetOpponent; +import mage.util.CardUtil; -import java.util.Set; import java.util.UUID; -import mage.ApprovingObject; /** * @author LevelX2 @@ -33,9 +34,8 @@ public final class TalentOfTheTelepath extends CardImpl { // Spell mastery — If there are two or more instant and/or // sorcery cards in your graveyard, you may cast up to two revealed instant // and/or sorcery cards instead of one. - getSpellAbility().addEffect(new TalentOfTheTelepathEffect()); - getSpellAbility().addTarget(new TargetOpponent()); - + this.getSpellAbility().addEffect(new TalentOfTheTelepathEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); } private TalentOfTheTelepath(final TalentOfTheTelepath card) { @@ -54,12 +54,12 @@ class TalentOfTheTelepathEffect extends OneShotEffect { public TalentOfTheTelepathEffect() { super(Outcome.PlayForFree); - this.staticText = "Target opponent reveals the top seven cards of their " - + "library. You may cast an instant or sorcery card from among them " - + "without paying its mana cost. Then that player puts the rest into their graveyard. " - + "
Spell mastery — If there are two or more instant " - + "and/or sorcery cards in your graveyard, you may cast up to two " - + "revealed instant and/or sorcery cards instead of one."; + this.staticText = "Target opponent reveals the top seven cards of their " + + "library. You may cast an instant or sorcery spell from among them " + + "without paying its mana cost. Then that player puts the rest into their graveyard. " + + "
Spell mastery — If there are two or more instant " + + "and/or sorcery cards in your graveyard, you may cast up to two " + + "instant and/or sorcery spells from among the revealed cards instead of one."; } public TalentOfTheTelepathEffect(final TalentOfTheTelepathEffect effect) { @@ -73,60 +73,19 @@ class TalentOfTheTelepathEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Cards cardsToCast = new CardsImpl(); - Player targetOpponent = game.getPlayer(targetPointer.getFirst(game, source)); - MageObject sourceObject = source.getSourceObject(game); - if (targetOpponent != null && sourceObject != null) { - Set allCards = targetOpponent.getLibrary().getTopCards(game, 7); - Cards cards = new CardsImpl(allCards); - targetOpponent.revealCards(sourceObject.getIdName() + " - " - + targetOpponent.getName() + "'s top library cards", cards, game); - for (Card card : allCards) { - if (filter.match(card, game)) { - cardsToCast.add(card); - } - } - // cast an instant or sorcery for free - if (!cardsToCast.isEmpty()) { - int numberOfSpells = 1; - if (SpellMasteryCondition.instance.apply(game, source)) { - numberOfSpells++; - } - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - TargetCard target = new TargetCard(Zone.LIBRARY, filter); // zone should be ignored here - target.setNotTarget(true); - while (controller.canRespond() - && numberOfSpells > 0 - && !cardsToCast.isEmpty() - && controller.chooseUse(outcome, "Cast an instant or sorcery card " - + "from among them for free?", source, game) - && controller.choose(Outcome.PlayForFree, cardsToCast, target, game)) { - Card card = cardsToCast.get(target.getFirstTarget(), game); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - cardsToCast.remove(card); - if (cardWasCast) { - numberOfSpells--; - allCards.remove(card); - } - } else { - break; - } - if (!controller.canRespond()) { - return false; - } - target.clearChosen(); - } - } - } - - targetOpponent.moveCards(allCards, Zone.GRAVEYARD, source, game); - return true; + Player controller = game.getPlayer(source.getControllerId()); + Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); + if (controller == null || opponent == null) { + return false; } - return false; + Cards cards = new CardsImpl(opponent.getLibrary().getTopCards(game, 7)); + opponent.revealCards(source, cards, game); + CardUtil.castMultipleWithAttributeForFree( + controller, source, game, cards, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, + SpellMasteryCondition.instance.apply(game, source) ? 2 : 1 + ); + cards.retainZone(Zone.LIBRARY, game); + opponent.moveCards(cards, Zone.GRAVEYARD, source, game); + return true; } } diff --git a/Mage.Sets/src/mage/cards/t/TheDragonKamiReborn.java b/Mage.Sets/src/mage/cards/t/TheDragonKamiReborn.java new file mode 100644 index 00000000000..82fab2b5a27 --- /dev/null +++ b/Mage.Sets/src/mage/cards/t/TheDragonKamiReborn.java @@ -0,0 +1,96 @@ +package mage.cards.t; + +import mage.abilities.Ability; +import mage.abilities.common.SagaAbility; +import mage.abilities.effects.Effects; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.ExileSagaAndReturnTransformedEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.keyword.TransformAbility; +import mage.cards.*; +import mage.constants.*; +import mage.counters.CounterType; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.target.common.TargetCardInLibrary; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class TheDragonKamiReborn extends CardImpl { + + public TheDragonKamiReborn(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); + + this.subtype.add(SubType.SAGA); + this.secondSideCardClazz = mage.cards.d.DragonKamisEgg.class; + + // (As this Saga enters and after your draw step, add a lore counter.) + SagaAbility sagaAbility = new SagaAbility(this); + + // I, II — You gain 2 life. Look at the top three cards of your library. Exile one of them face down with a hatching counter on it, then put the rest on the bottom of your library in any order. + sagaAbility.addChapterEffect( + this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, + new Effects(new GainLifeEffect(2), new TheDragonKamiRebornEffect()) + ); + + // III — Exile this Saga, then return it to the battlefield transformed under your control. + this.addAbility(new TransformAbility()); + sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, new ExileSagaAndReturnTransformedEffect()); + + this.addAbility(sagaAbility); + } + + private TheDragonKamiReborn(final TheDragonKamiReborn card) { + super(card); + } + + @Override + public TheDragonKamiReborn copy() { + return new TheDragonKamiReborn(this); + } +} + +class TheDragonKamiRebornEffect extends OneShotEffect { + + TheDragonKamiRebornEffect() { + super(Outcome.Benefit); + staticText = "Look at the top three cards of your library. Exile one of them face down " + + "with a hatching counter on it, then put the rest on the bottom of your library in any order"; + } + + private TheDragonKamiRebornEffect(final TheDragonKamiRebornEffect effect) { + super(effect); + } + + @Override + public TheDragonKamiRebornEffect copy() { + return new TheDragonKamiRebornEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + if (player == null) { + return false; + } + Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 3)); + if (cards.isEmpty()) { + return false; + } + TargetCard target = new TargetCardInLibrary(); + player.choose(outcome, cards, target, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + player.moveCards(card, Zone.EXILED, source, game); + card.setFaceDown(true, game); + card.addCounters(CounterType.HATCHLING.createInstance(), source, game); + } + cards.retainZone(Zone.LIBRARY, game); + player.putCardsOnBottomOfLibrary(card, game, source, true); + return true; + } +} diff --git a/Mage.Sets/src/mage/cards/t/TwinningGlass.java b/Mage.Sets/src/mage/cards/t/TwinningGlass.java index b4ec11930c3..1b9512ea2fc 100644 --- a/Mage.Sets/src/mage/cards/t/TwinningGlass.java +++ b/Mage.Sets/src/mage/cards/t/TwinningGlass.java @@ -1,47 +1,45 @@ package mage.cards.t; -import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; 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.common.FilterNonlandCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.NamePredicate; +import mage.filter.FilterCard; +import mage.filter.predicate.Predicate; import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.Spell; -import mage.players.Player; -import mage.target.TargetCard; +import mage.util.CardUtil; import mage.watchers.common.SpellsCastWatcher; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; import java.util.UUID; -import java.util.stream.Collectors; /** * @author jeffwadsworth */ public final class TwinningGlass extends CardImpl { + private static final FilterCard filter = new FilterCard(); + + static { + filter.add(TwinningGlassPredicate.instance); + } + public TwinningGlass(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}"); // {1}, {T}: You may cast a nonland card from your hand without paying its mana cost if it has the same name as a spell that was cast this turn. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new TwinningGlassEffect(), new ManaCostsImpl("{1}")); + Ability ability = new SimpleActivatedAbility( + new CastFromHandForFreeEffect(filter).setText( + "you may cast a spell from your hand without paying its mana cost " + + "if it has the same name as a spell that was cast this turn" + ), new GenericManaCost(1) + ); ability.addCost(new TapSourceCost()); this.addAbility(ability, new SpellsCastWatcher()); - } private TwinningGlass(final TwinningGlass card) { @@ -54,67 +52,14 @@ public final class TwinningGlass extends CardImpl { } } -class TwinningGlassEffect extends OneShotEffect { - - public TwinningGlassEffect() { - super(Outcome.PlayForFree); - this.staticText = "You may cast a nonland card from your hand " - + "without paying its mana cost if it has the same name " - + "as a spell that was cast this turn"; - } - - public TwinningGlassEffect(final TwinningGlassEffect effect) { - super(effect); - } +enum TwinningGlassPredicate implements Predicate { + instance; @Override - public TwinningGlassEffect copy() { - return new TwinningGlassEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - List spells = new ArrayList<>(); - Permanent twinningGlass = game.getPermanent(source.getSourceId()); - Player controller = game.getPlayer(source.getControllerId()); + public boolean apply(Card input, Game game) { SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); - if (twinningGlass == null) { - twinningGlass = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); - } - if (twinningGlass != null - && controller != null - && watcher != null) { - for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) { - if (watcher.getSpellsCastThisTurn(playerId) != null) { - for (Spell spell : watcher.getSpellsCastThisTurn(playerId)) { - spells.add(spell); - } - } - } - if (spells.isEmpty()) { - return false; - } - List predicates = spells.stream() - .map(Spell::getName) - .filter(Objects::nonNull) - .filter(s -> !s.isEmpty()) - .map(NamePredicate::new) - .collect(Collectors.toList()); - FilterNonlandCard filterCard = new FilterNonlandCard("nonland card that was cast this turn"); - filterCard.add(Predicates.or(predicates)); - TargetCard target = new TargetCard(0, 1, Zone.HAND, filterCard); - target.withChooseHint("free cast"); - if (controller.choose(Outcome.PlayForFree, controller.getHand(), target, game)) { - Card chosenCard = game.getCard(target.getFirstTarget()); - if (chosenCard != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + chosenCard.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(chosenCard, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + chosenCard.getId(), null); - return cardWasCast; - } - } - } - return false; + return watcher != null && watcher + .getAllSpellsCastThisTurn() + .anyMatch(spell -> CardUtil.haveSameNames(spell, input)); } } diff --git a/Mage.Sets/src/mage/cards/v/VelomachusLorehold.java b/Mage.Sets/src/mage/cards/v/VelomachusLorehold.java index 1e3e42c0fad..0fdcc0d99b0 100644 --- a/Mage.Sets/src/mage/cards/v/VelomachusLorehold.java +++ b/Mage.Sets/src/mage/cards/v/VelomachusLorehold.java @@ -1,6 +1,5 @@ package mage.cards.v; -import mage.ApprovingObject; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksTriggeredAbility; @@ -8,7 +7,10 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.VigilanceAbility; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; @@ -16,10 +18,8 @@ import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.TargetCard; -import mage.target.common.TargetCardInLibrary; +import mage.util.CardUtil; -import java.util.Set; import java.util.UUID; /** @@ -84,26 +84,11 @@ class VelomachusLoreholdEffect extends OneShotEffect { if (controller == null || permanent == null) { return false; } - Set cardsSet = controller.getLibrary().getTopCards(game, 7); - Cards cards = new CardsImpl(cardsSet); - FilterCard filter = new FilterInstantOrSorceryCard( - "instant or sorcery card with mana value " + permanent.getPower().getValue() + " or less" - ); + Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 7)); + FilterCard filter = new FilterInstantOrSorceryCard(); filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, permanent.getPower().getValue() + 1)); - TargetCard target = new TargetCardInLibrary(0, 1, filter); - controller.choose(Outcome.PlayForFree, cards, target, game); - Card card = controller.getLibrary().getCard(target.getFirstTarget(), game); - if (card == null) { - controller.putCardsOnBottomOfLibrary(cards, game, source, false); - return true; - } - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - if (cardWasCast) { - cards.remove(card); - } + CardUtil.castSpellWithAttributesForFree(controller, source, game, cards, filter); + cards.retainZone(Zone.LIBRARY, game); controller.putCardsOnBottomOfLibrary(cards, game, source, false); return true; } diff --git a/Mage.Sets/src/mage/cards/v/VillainousWealth.java b/Mage.Sets/src/mage/cards/v/VillainousWealth.java index 97f078d8586..0425449da67 100644 --- a/Mage.Sets/src/mage/cards/v/VillainousWealth.java +++ b/Mage.Sets/src/mage/cards/v/VillainousWealth.java @@ -1,24 +1,23 @@ package mage.cards.v; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.common.FilterNonlandCard; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.players.Player; -import mage.target.common.TargetCardInExile; import mage.target.common.TargetOpponent; import mage.util.CardUtil; import java.util.UUID; -import mage.ApprovingObject; /** * @author LevelX2 @@ -30,9 +29,8 @@ public final class VillainousWealth extends CardImpl { // Target opponent exiles the top X cards of their library. You may cast any number of nonland cards // with converted mana cost X or less from among them without paying their mana cost. - this.getSpellAbility().addTarget(new TargetOpponent()); this.getSpellAbility().addEffect(new VillainousWealthEffect()); - + this.getSpellAbility().addTarget(new TargetOpponent()); } private VillainousWealth(final VillainousWealth card) { @@ -66,48 +64,16 @@ class VillainousWealthEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject mageObject = game.getObject(source.getSourceId()); - if (controller != null && mageObject != null) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - FilterCard filter = new FilterNonlandCard(); - filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); - UUID exileId = CardUtil.getCardExileZoneId(game, source); - if (player != null) { - Cards cardsToExile = new CardsImpl(); - cardsToExile.addAll(player.getLibrary().getTopCards(game, source.getManaCostsToPay().getX())); - controller.moveCards(cardsToExile, Zone.EXILED, source, game); - if (controller.chooseUse(Outcome.PlayForFree, "Cast cards exiled with " + mageObject.getLogName() - + " without paying its mana cost?", source, game)) { - OuterLoop: - while (cardsToExile.count(filter, game) > 0) { - if (!controller.canRespond()) { - return false; - } - TargetCardInExile target = new TargetCardInExile(0, 1, filter, exileId, false); - target.setNotTarget(true); - while (controller.canRespond() - && cardsToExile.count(filter, game) > 0 - && controller.choose(Outcome.PlayForFree, cardsToExile, target, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - Boolean cardWasCast = controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - if (cardWasCast) { - cardsToExile.remove(card); - } - } else { - break OuterLoop; - } - target.clearChosen(); - } - } - } - } - return true; + Player opponent = game.getPlayer(targetPointer.getFirst(game, source)); + int xValue = source.getManaCostsToPay().getX(); + if (controller == null || opponent == null || xValue < 1) { + return false; } - - return false; + Cards cards = new CardsImpl(opponent.getLibrary().getTopCards(game, xValue)); + opponent.moveCards(cards, Zone.EXILED, source, game); + FilterCard filter = new FilterCard(); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, source.getManaCostsToPay().getX() + 1)); + CardUtil.castMultipleWithAttributeForFree(controller, source, game, cards, filter); + return true; } } diff --git a/Mage.Sets/src/mage/cards/w/WildfireEternal.java b/Mage.Sets/src/mage/cards/w/WildfireEternal.java index 9bf0ef31a1b..ba47cf24e72 100644 --- a/Mage.Sets/src/mage/cards/w/WildfireEternal.java +++ b/Mage.Sets/src/mage/cards/w/WildfireEternal.java @@ -1,30 +1,25 @@ package mage.cards.w; -import java.util.UUID; -import mage.ApprovingObject; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.abilities.keyword.AfflictAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterInstantOrSorceryCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetCardInHand; + +import java.util.UUID; /** - * * @author spjspj */ public final class WildfireEternal extends CardImpl { + private static final FilterCard filter = new FilterInstantOrSorceryCard("an instant or sorcery spell"); + public WildfireEternal(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}"); @@ -35,10 +30,12 @@ public final class WildfireEternal extends CardImpl { this.toughness = new MageInt(4); // Afflict 4 - addAbility(new AfflictAbility(4)); + this.addAbility(new AfflictAbility(4)); // Whenever Wildfire Eternal attacks and isn't blocked, you may cast an instant or sorcery card from your hand without paying its mana cost. - this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility(new WildfireEternalCastEffect(), false, true)); + this.addAbility(new AttacksAndIsNotBlockedTriggeredAbility( + new CastFromHandForFreeEffect(filter), false, true + )); } private WildfireEternal(final WildfireEternal card) { @@ -50,45 +47,3 @@ public final class WildfireEternal extends CardImpl { return new WildfireEternal(this); } } - -class WildfireEternalCastEffect extends OneShotEffect { - - public WildfireEternalCastEffect() { - super(Outcome.Benefit); - this.staticText = "you may cast an instant or sorcery card " - + "from your hand without paying its mana cost"; - } - - public WildfireEternalCastEffect(final WildfireEternalCastEffect effect) { - super(effect); - } - - @Override - public WildfireEternalCastEffect copy() { - return new WildfireEternalCastEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - FilterCard filter = new FilterInstantOrSorceryCard(); - int cardsToCast = controller.getHand().count(filter, source.getControllerId(), source.getSourceId(), game); - if (cardsToCast > 0 - && controller.chooseUse(outcome, "Cast an instant or sorcery card from your " - + "hand without paying its mana cost?", source, game)) { - TargetCardInHand target = new TargetCardInHand(filter); - controller.chooseTarget(outcome, target, source, game); - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - } - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/y/YahennisExpertise.java b/Mage.Sets/src/mage/cards/y/YahennisExpertise.java index 526e36f83d0..29fea75f30f 100644 --- a/Mage.Sets/src/mage/cards/y/YahennisExpertise.java +++ b/Mage.Sets/src/mage/cards/y/YahennisExpertise.java @@ -1,29 +1,36 @@ - - package mage.cards.y; -import java.util.UUID; import mage.abilities.effects.common.continuous.BoostAllEffect; -import mage.abilities.effects.common.cost.CastWithoutPayingManaCostEffect; +import mage.abilities.effects.common.cost.CastFromHandForFreeEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.Duration; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ManaValuePredicate; + +import java.util.UUID; /** * @author fireshoes */ public final class YahennisExpertise extends CardImpl { + private static final FilterCard filter = new FilterCard("a spell with mana value 3 or less"); + + static { + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + } + public YahennisExpertise(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}"); - // All creatures get -3/-3 until end of turn. this.getSpellAbility().addEffect(new BoostAllEffect(-3, -3, Duration.EndOfTurn)); // You may cast a card with converted mana cost 3 or less from your hand without paying its mana cost. - this.getSpellAbility().addEffect(new CastWithoutPayingManaCostEffect(3).concatBy("
")); + this.getSpellAbility().addEffect(new CastFromHandForFreeEffect(filter).concatBy("
")); } private YahennisExpertise(final YahennisExpertise card) { diff --git a/Mage.Sets/src/mage/sets/CrimsonVowCommander.java b/Mage.Sets/src/mage/sets/CrimsonVowCommander.java index c07dbcfa52b..1046a181864 100644 --- a/Mage.Sets/src/mage/sets/CrimsonVowCommander.java +++ b/Mage.Sets/src/mage/sets/CrimsonVowCommander.java @@ -25,7 +25,8 @@ public final class CrimsonVowCommander extends ExpansionSet { cards.add(new SetCardInfo("Anowon, the Ruin Sage", 118, Rarity.RARE, mage.cards.a.AnowonTheRuinSage.class)); cards.add(new SetCardInfo("Arcane Denial", 102, Rarity.COMMON, mage.cards.a.ArcaneDenial.class)); cards.add(new SetCardInfo("Arcane Signet", 159, Rarity.COMMON, mage.cards.a.ArcaneSignet.class)); - cards.add(new SetCardInfo("Arterial Alchemy", 23, Rarity.RARE, mage.cards.a.ArterialAlchemy.class)); + cards.add(new SetCardInfo("Arterial Alchemy", 23, Rarity.RARE, mage.cards.a.ArterialAlchemy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Arterial Alchemy", 61, Rarity.RARE, mage.cards.a.ArterialAlchemy.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Avacyn's Judgment", 142, Rarity.RARE, mage.cards.a.AvacynsJudgment.class)); cards.add(new SetCardInfo("Azorius Chancery", 171, Rarity.UNCOMMON, mage.cards.a.AzoriusChancery.class)); cards.add(new SetCardInfo("Azorius Locket", 160, Rarity.COMMON, mage.cards.a.AzoriusLocket.class)); @@ -38,8 +39,10 @@ public final class CrimsonVowCommander extends ExpansionSet { cards.add(new SetCardInfo("Bloodsworn Steward", 144, Rarity.RARE, mage.cards.b.BloodswornSteward.class)); cards.add(new SetCardInfo("Bloodtracker", 122, Rarity.RARE, mage.cards.b.Bloodtracker.class)); cards.add(new SetCardInfo("Boreas Charger", 79, Rarity.RARE, mage.cards.b.BoreasCharger.class)); - cards.add(new SetCardInfo("Breath of the Sleepless", 11, Rarity.RARE, mage.cards.b.BreathOfTheSleepless.class)); - cards.add(new SetCardInfo("Breathkeeper Seraph", 31, Rarity.RARE, mage.cards.b.BreathkeeperSeraph.class)); + cards.add(new SetCardInfo("Breath of the Sleepless", 11, Rarity.RARE, mage.cards.b.BreathOfTheSleepless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Breath of the Sleepless", 49, Rarity.RARE, mage.cards.b.BreathOfTheSleepless.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Breathkeeper Seraph", 31, Rarity.RARE, mage.cards.b.BreathkeeperSeraph.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Breathkeeper Seraph", 69, Rarity.RARE, mage.cards.b.BreathkeeperSeraph.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Butcher of Malakir", 123, Rarity.RARE, mage.cards.b.ButcherOfMalakir.class)); cards.add(new SetCardInfo("Bygone Bishop", 80, Rarity.RARE, mage.cards.b.BygoneBishop.class)); cards.add(new SetCardInfo("Champion of Dusk", 124, Rarity.RARE, mage.cards.c.ChampionOfDusk.class)); @@ -48,22 +51,27 @@ public final class CrimsonVowCommander extends ExpansionSet { cards.add(new SetCardInfo("Commander's Sphere", 163, Rarity.COMMON, mage.cards.c.CommandersSphere.class)); cards.add(new SetCardInfo("Cordial Vampire", 125, Rarity.RARE, mage.cards.c.CordialVampire.class)); cards.add(new SetCardInfo("Crimson Honor Guard", 145, Rarity.RARE, mage.cards.c.CrimsonHonorGuard.class)); - cards.add(new SetCardInfo("Crossway Troublemakers", 17, Rarity.RARE, mage.cards.c.CrosswayTroublemakers.class)); + cards.add(new SetCardInfo("Crossway Troublemakers", 17, Rarity.RARE, mage.cards.c.CrosswayTroublemakers.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Crossway Troublemakers", 55, Rarity.RARE, mage.cards.c.CrosswayTroublemakers.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Crush Contraband", 81, Rarity.UNCOMMON, mage.cards.c.CrushContraband.class)); cards.add(new SetCardInfo("Custodi Soulbinders", 82, Rarity.RARE, mage.cards.c.CustodiSoulbinders.class)); cards.add(new SetCardInfo("Custodi Squire", 83, Rarity.COMMON, mage.cards.c.CustodiSquire.class)); cards.add(new SetCardInfo("Damnable Pact", 126, Rarity.RARE, mage.cards.d.DamnablePact.class)); cards.add(new SetCardInfo("Dark Impostor", 127, Rarity.RARE, mage.cards.d.DarkImpostor.class)); cards.add(new SetCardInfo("Darksteel Mutation", 84, Rarity.UNCOMMON, mage.cards.d.DarksteelMutation.class)); - cards.add(new SetCardInfo("Disorder in the Court", 29, Rarity.RARE, mage.cards.d.DisorderInTheCourt.class)); + cards.add(new SetCardInfo("Disorder in the Court", 29, Rarity.RARE, mage.cards.d.DisorderInTheCourt.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Disorder in the Court", 67, Rarity.RARE, mage.cards.d.DisorderInTheCourt.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Distant Melody", 103, Rarity.COMMON, mage.cards.d.DistantMelody.class)); cards.add(new SetCardInfo("Donal, Herald of Wings", 3, Rarity.MYTHIC, mage.cards.d.DonalHeraldOfWings.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Donal, Herald of Wings", 41, Rarity.MYTHIC, mage.cards.d.DonalHeraldOfWings.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Doom Weaver", 34, Rarity.RARE, mage.cards.d.DoomWeaver.class)); + cards.add(new SetCardInfo("Doom Weaver", 34, Rarity.RARE, mage.cards.d.DoomWeaver.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Doom Weaver", 72, Rarity.RARE, mage.cards.d.DoomWeaver.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dovin, Grand Arbiter", 153, Rarity.MYTHIC, mage.cards.d.DovinGrandArbiter.class)); cards.add(new SetCardInfo("Drogskol Captain", 154, Rarity.UNCOMMON, mage.cards.d.DrogskolCaptain.class)); - cards.add(new SetCardInfo("Drogskol Reinforcements", 5, Rarity.RARE, mage.cards.d.DrogskolReinforcements.class)); - cards.add(new SetCardInfo("Ethereal Investigator", 12, Rarity.RARE, mage.cards.e.EtherealInvestigator.class)); + cards.add(new SetCardInfo("Drogskol Reinforcements", 43, Rarity.RARE, mage.cards.d.DrogskolReinforcements.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Drogskol Reinforcements", 5, Rarity.RARE, mage.cards.d.DrogskolReinforcements.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ethereal Investigator", 12, Rarity.RARE, mage.cards.e.EtherealInvestigator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ethereal Investigator", 50, Rarity.RARE, mage.cards.e.EtherealInvestigator.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Exotic Orchard", 173, Rarity.RARE, mage.cards.e.ExoticOrchard.class)); cards.add(new SetCardInfo("Falkenrath Gorger", 146, Rarity.RARE, mage.cards.f.FalkenrathGorger.class)); cards.add(new SetCardInfo("Falkenrath Noble", 128, Rarity.UNCOMMON, mage.cards.f.FalkenrathNoble.class)); @@ -76,30 +84,42 @@ public final class CrimsonVowCommander extends ExpansionSet { cards.add(new SetCardInfo("Geist of Saint Traft", 155, Rarity.MYTHIC, mage.cards.g.GeistOfSaintTraft.class)); cards.add(new SetCardInfo("Ghostly Pilferer", 105, Rarity.RARE, mage.cards.g.GhostlyPilferer.class)); cards.add(new SetCardInfo("Ghostly Prison", 87, Rarity.UNCOMMON, mage.cards.g.GhostlyPrison.class)); - cards.add(new SetCardInfo("Glass-Cast Heart", 18, Rarity.RARE, mage.cards.g.GlassCastHeart.class)); + cards.add(new SetCardInfo("Glass-Cast Heart", 18, Rarity.RARE, mage.cards.g.GlassCastHeart.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Glass-Cast Heart", 56, Rarity.RARE, mage.cards.g.GlassCastHeart.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hallowed Spiritkeeper", 88, Rarity.RARE, mage.cards.h.HallowedSpiritkeeper.class)); cards.add(new SetCardInfo("Hanged Executioner", 89, Rarity.RARE, mage.cards.h.HangedExecutioner.class)); - cards.add(new SetCardInfo("Haunted Library", 6, Rarity.RARE, mage.cards.h.HauntedLibrary.class)); - cards.add(new SetCardInfo("Haunting Imitation", 13, Rarity.RARE, mage.cards.h.HauntingImitation.class)); - cards.add(new SetCardInfo("Hollowhenge Overlord", 36, Rarity.RARE, mage.cards.h.HollowhengeOverlord.class)); - cards.add(new SetCardInfo("Imperious Mindbreaker", 33, Rarity.RARE, mage.cards.i.ImperiousMindbreaker.class)); - cards.add(new SetCardInfo("Imposing Grandeur", 24, Rarity.RARE, mage.cards.i.ImposingGrandeur.class)); + cards.add(new SetCardInfo("Haunted Library", 44, Rarity.RARE, mage.cards.h.HauntedLibrary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Haunted Library", 6, Rarity.RARE, mage.cards.h.HauntedLibrary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Haunting Imitation", 13, Rarity.RARE, mage.cards.h.HauntingImitation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Haunting Imitation", 51, Rarity.RARE, mage.cards.h.HauntingImitation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hollowhenge Overlord", 36, Rarity.RARE, mage.cards.h.HollowhengeOverlord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hollowhenge Overlord", 74, Rarity.RARE, mage.cards.h.HollowhengeOverlord.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Imperious Mindbreaker", 33, Rarity.RARE, mage.cards.i.ImperiousMindbreaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Imperious Mindbreaker", 71, Rarity.RARE, mage.cards.i.ImperiousMindbreaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Imposing Grandeur", 24, Rarity.RARE, mage.cards.i.ImposingGrandeur.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Imposing Grandeur", 62, Rarity.RARE, mage.cards.i.ImposingGrandeur.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Imprisoned in the Moon", 106, Rarity.RARE, mage.cards.i.ImprisonedInTheMoon.class)); cards.add(new SetCardInfo("Indulgent Aristocrat", 130, Rarity.UNCOMMON, mage.cards.i.IndulgentAristocrat.class)); - cards.add(new SetCardInfo("Kamber, the Plunderer", 19, Rarity.RARE, mage.cards.k.KamberThePlunderer.class)); + cards.add(new SetCardInfo("Kamber, the Plunderer", 19, Rarity.RARE, mage.cards.k.KamberThePlunderer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kamber, the Plunderer", 57, Rarity.RARE, mage.cards.k.KamberThePlunderer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kami of the Crescent Moon", 107, Rarity.RARE, mage.cards.k.KamiOfTheCrescentMoon.class)); cards.add(new SetCardInfo("Karmic Guide", 90, Rarity.RARE, mage.cards.k.KarmicGuide.class)); cards.add(new SetCardInfo("Kirtar's Wrath", 91, Rarity.RARE, mage.cards.k.KirtarsWrath.class)); cards.add(new SetCardInfo("Knight of the White Orchid", 92, Rarity.RARE, mage.cards.k.KnightOfTheWhiteOrchid.class)); - cards.add(new SetCardInfo("Laurine, the Diversion", 25, Rarity.RARE, mage.cards.l.LaurineTheDiversion.class)); + cards.add(new SetCardInfo("Laurine, the Diversion", 25, Rarity.RARE, mage.cards.l.LaurineTheDiversion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Laurine, the Diversion", 63, Rarity.RARE, mage.cards.l.LaurineTheDiversion.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Malakir Bloodwitch", 131, Rarity.RARE, mage.cards.m.MalakirBloodwitch.class)); cards.add(new SetCardInfo("Marble Diamond", 165, Rarity.COMMON, mage.cards.m.MarbleDiamond.class)); - cards.add(new SetCardInfo("Markov Enforcer", 26, Rarity.RARE, mage.cards.m.MarkovEnforcer.class)); + cards.add(new SetCardInfo("Markov Enforcer", 26, Rarity.RARE, mage.cards.m.MarkovEnforcer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Markov Enforcer", 64, Rarity.RARE, mage.cards.m.MarkovEnforcer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mentor of the Meek", 93, Rarity.RARE, mage.cards.m.MentorOfTheMeek.class)); - cards.add(new SetCardInfo("Midnight Arsonist", 27, Rarity.RARE, mage.cards.m.MidnightArsonist.class)); + cards.add(new SetCardInfo("Midnight Arsonist", 27, Rarity.RARE, mage.cards.m.MidnightArsonist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Midnight Arsonist", 65, Rarity.RARE, mage.cards.m.MidnightArsonist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Midnight Clock", 108, Rarity.RARE, mage.cards.m.MidnightClock.class)); - cards.add(new SetCardInfo("Millicent, Restless Revenant", 1, Rarity.MYTHIC, mage.cards.m.MillicentRestlessRevenant.class)); - cards.add(new SetCardInfo("Mirage Phalanx", 35, Rarity.RARE, mage.cards.m.MiragePhalanx.class)); + cards.add(new SetCardInfo("Millicent, Restless Revenant", 1, Rarity.MYTHIC, mage.cards.m.MillicentRestlessRevenant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Millicent, Restless Revenant", 39, Rarity.MYTHIC, mage.cards.m.MillicentRestlessRevenant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirage Phalanx", 35, Rarity.RARE, mage.cards.m.MiragePhalanx.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirage Phalanx", 73, Rarity.RARE, mage.cards.m.MiragePhalanx.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mirror Entity", 94, Rarity.RARE, mage.cards.m.MirrorEntity.class)); cards.add(new SetCardInfo("Mob Rule", 147, Rarity.RARE, mage.cards.m.MobRule.class)); cards.add(new SetCardInfo("Molten Echoes", 148, Rarity.RARE, mage.cards.m.MoltenEchoes.class)); @@ -109,47 +129,57 @@ public final class CrimsonVowCommander extends ExpansionSet { cards.add(new SetCardInfo("Necropolis Regent", 132, Rarity.MYTHIC, mage.cards.n.NecropolisRegent.class)); cards.add(new SetCardInfo("Night's Whisper", 133, Rarity.COMMON, mage.cards.n.NightsWhisper.class)); cards.add(new SetCardInfo("Nirkana Revenant", 134, Rarity.MYTHIC, mage.cards.n.NirkanaRevenant.class)); - cards.add(new SetCardInfo("Occult Epiphany", 14, Rarity.RARE, mage.cards.o.OccultEpiphany.class)); - cards.add(new SetCardInfo("Olivia's Wrath", 20, Rarity.RARE, mage.cards.o.OliviasWrath.class)); + cards.add(new SetCardInfo("Occult Epiphany", 14, Rarity.RARE, mage.cards.o.OccultEpiphany.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Occult Epiphany", 52, Rarity.RARE, mage.cards.o.OccultEpiphany.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Olivia's Wrath", 20, Rarity.RARE, mage.cards.o.OliviasWrath.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Olivia's Wrath", 58, Rarity.RARE, mage.cards.o.OliviasWrath.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Oyobi, Who Split the Heavens", 95, Rarity.RARE, mage.cards.o.OyobiWhoSplitTheHeavens.class)); cards.add(new SetCardInfo("Path of Ancestry", 177, Rarity.COMMON, mage.cards.p.PathOfAncestry.class)); cards.add(new SetCardInfo("Patron of the Vein", 135, Rarity.RARE, mage.cards.p.PatronOfTheVein.class)); cards.add(new SetCardInfo("Port Town", 178, Rarity.RARE, mage.cards.p.PortTown.class)); cards.add(new SetCardInfo("Prairie Stream", 179, Rarity.RARE, mage.cards.p.PrairieStream.class)); - cards.add(new SetCardInfo("Predators' Hour", 21, Rarity.RARE, mage.cards.p.PredatorsHour.class)); - cards.add(new SetCardInfo("Priest of the Blessed Graf", 7, Rarity.RARE, mage.cards.p.PriestOfTheBlessedGraf.class)); + cards.add(new SetCardInfo("Predators' Hour", 21, Rarity.RARE, mage.cards.p.PredatorsHour.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Predators' Hour", 59, Rarity.RARE, mage.cards.p.PredatorsHour.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Priest of the Blessed Graf", 45, Rarity.RARE, mage.cards.p.PriestOfTheBlessedGraf.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Priest of the Blessed Graf", 7, Rarity.RARE, mage.cards.p.PriestOfTheBlessedGraf.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Promise of Bunrei", 96, Rarity.RARE, mage.cards.p.PromiseOfBunrei.class)); - cards.add(new SetCardInfo("Rakdos Carnarium", 180, Rarity.UNCOMMON, mage.cards.r.RakdosCarnarium.class)); + cards.add(new SetCardInfo("Rakdos Carnarium", 180, Rarity.COMMON, mage.cards.r.RakdosCarnarium.class)); cards.add(new SetCardInfo("Rakdos Charm", 156, Rarity.UNCOMMON, mage.cards.r.RakdosCharm.class)); cards.add(new SetCardInfo("Rakdos Signet", 166, Rarity.UNCOMMON, mage.cards.r.RakdosSignet.class)); cards.add(new SetCardInfo("Rakish Heir", 149, Rarity.UNCOMMON, mage.cards.r.RakishHeir.class)); cards.add(new SetCardInfo("Rattlechains", 110, Rarity.RARE, mage.cards.r.Rattlechains.class)); cards.add(new SetCardInfo("Reconnaissance Mission", 111, Rarity.UNCOMMON, mage.cards.r.ReconnaissanceMission.class)); cards.add(new SetCardInfo("Remorseful Cleric", 97, Rarity.RARE, mage.cards.r.RemorsefulCleric.class)); - cards.add(new SetCardInfo("Rhoda, Geist Avenger", 8, Rarity.RARE, mage.cards.r.RhodaGeistAvenger.class)); + cards.add(new SetCardInfo("Rhoda, Geist Avenger", 46, Rarity.RARE, mage.cards.r.RhodaGeistAvenger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Rhoda, Geist Avenger", 8, Rarity.RARE, mage.cards.r.RhodaGeistAvenger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sanctum Seeker", 136, Rarity.RARE, mage.cards.s.SanctumSeeker.class)); - cards.add(new SetCardInfo("Scion of Opulence", 28, Rarity.RARE, mage.cards.s.ScionOfOpulence.class)); + cards.add(new SetCardInfo("Scion of Opulence", 28, Rarity.RARE, mage.cards.s.ScionOfOpulence.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scion of Opulence", 66, Rarity.RARE, mage.cards.s.ScionOfOpulence.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shacklegeist", 112, Rarity.RARE, mage.cards.s.Shacklegeist.class)); cards.add(new SetCardInfo("Shadowblood Ridge", 181, Rarity.RARE, mage.cards.s.ShadowbloodRidge.class)); cards.add(new SetCardInfo("Shadowgrange Archfiend", 22, Rarity.RARE, mage.cards.s.ShadowgrangeArchfiend.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Shadowgrange Archfiend", 60, Rarity.RARE, mage.cards.s.ShadowgrangeArchfiend.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Sinister Waltz", 30, Rarity.RARE, mage.cards.s.SinisterWaltz.class)); + cards.add(new SetCardInfo("Sinister Waltz", 30, Rarity.RARE, mage.cards.s.SinisterWaltz.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sinister Waltz", 68, Rarity.RARE, mage.cards.s.SinisterWaltz.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Sire of the Storm", 113, Rarity.UNCOMMON, mage.cards.s.SireOfTheStorm.class)); cards.add(new SetCardInfo("Sky Diamond", 167, Rarity.COMMON, mage.cards.s.SkyDiamond.class)); cards.add(new SetCardInfo("Skycloud Expanse", 182, Rarity.RARE, mage.cards.s.SkycloudExpanse.class)); cards.add(new SetCardInfo("Smoldering Marsh", 183, Rarity.RARE, mage.cards.s.SmolderingMarsh.class)); cards.add(new SetCardInfo("Sol Ring", 168, Rarity.UNCOMMON, mage.cards.s.SolRing.class)); + cards.add(new SetCardInfo("Spectral Arcanist", 15, Rarity.RARE, mage.cards.s.SpectralArcanist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spectral Arcanist", 53, Rarity.RARE, mage.cards.s.SpectralArcanist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spectral Sailor", 114, Rarity.UNCOMMON, mage.cards.s.SpectralSailor.class)); cards.add(new SetCardInfo("Spectral Shepherd", 98, Rarity.UNCOMMON, mage.cards.s.SpectralShepherd.class)); cards.add(new SetCardInfo("Stensia Masquerade", 150, Rarity.UNCOMMON, mage.cards.s.StensiaMasquerade.class)); - cards.add(new SetCardInfo("Storm of Souls", 9, Rarity.RARE, mage.cards.s.StormOfSouls.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Storm of Souls", 47, Rarity.RARE, mage.cards.s.StormOfSouls.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Storm of Souls", 9, Rarity.RARE, mage.cards.s.StormOfSouls.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Strefan, Maurer Progenitor", 2, Rarity.MYTHIC, mage.cards.s.StrefanMaurerProgenitor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Strefan, Maurer Progenitor", 40, Rarity.MYTHIC, mage.cards.s.StrefanMaurerProgenitor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Stromkirk Captain", 157, Rarity.UNCOMMON, mage.cards.s.StromkirkCaptain.class)); cards.add(new SetCardInfo("Stromkirk Condemned", 137, Rarity.RARE, mage.cards.s.StromkirkCondemned.class)); cards.add(new SetCardInfo("Stromkirk Occultist", 151, Rarity.RARE, mage.cards.s.StromkirkOccultist.class)); - cards.add(new SetCardInfo("Sudden Salvation", 10, Rarity.RARE, mage.cards.s.SuddenSalvation.class)); + cards.add(new SetCardInfo("Sudden Salvation", 10, Rarity.RARE, mage.cards.s.SuddenSalvation.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sudden Salvation", 48, Rarity.RARE, mage.cards.s.SuddenSalvation.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Supreme Phantom", 115, Rarity.RARE, mage.cards.s.SupremePhantom.class)); cards.add(new SetCardInfo("Swiftfoot Boots", 169, Rarity.UNCOMMON, mage.cards.s.SwiftfootBoots.class)); cards.add(new SetCardInfo("Swords to Plowshares", 99, Rarity.UNCOMMON, mage.cards.s.SwordsToPlowshares.class)); @@ -157,12 +187,15 @@ public final class CrimsonVowCommander extends ExpansionSet { cards.add(new SetCardInfo("Temple of Enlightenment", 185, Rarity.RARE, mage.cards.t.TempleOfEnlightenment.class)); cards.add(new SetCardInfo("Temple of Malice", 186, Rarity.RARE, mage.cards.t.TempleOfMalice.class)); cards.add(new SetCardInfo("Temple of the False God", 187, Rarity.UNCOMMON, mage.cards.t.TempleOfTheFalseGod.class)); - cards.add(new SetCardInfo("Thundering Mightmare", 37, Rarity.RARE, mage.cards.t.ThunderingMightmare.class)); - cards.add(new SetCardInfo("Timin, Youthful Geist", 16, Rarity.RARE, mage.cards.t.TiminYouthfulGeist.class)); + cards.add(new SetCardInfo("Thundering Mightmare", 37, Rarity.RARE, mage.cards.t.ThunderingMightmare.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thundering Mightmare", 75, Rarity.RARE, mage.cards.t.ThunderingMightmare.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Timin, Youthful Geist", 16, Rarity.RARE, mage.cards.t.TiminYouthfulGeist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Timin, Youthful Geist", 54, Rarity.RARE, mage.cards.t.TiminYouthfulGeist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Timothar, Baron of Bats", 4, Rarity.MYTHIC, mage.cards.t.TimotharBaronOfBats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Timothar, Baron of Bats", 42, Rarity.MYTHIC, mage.cards.t.TimotharBaronOfBats.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Twilight Drover", 100, Rarity.RARE, mage.cards.t.TwilightDrover.class)); - cards.add(new SetCardInfo("Umbris, Fear Manifest", 38, Rarity.MYTHIC, mage.cards.u.UmbrisFearManifest.class)); + cards.add(new SetCardInfo("Umbris, Fear Manifest", 38, Rarity.MYTHIC, mage.cards.u.UmbrisFearManifest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Umbris, Fear Manifest", 76, Rarity.MYTHIC, mage.cards.u.UmbrisFearManifest.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Unclaimed Territory", 188, Rarity.UNCOMMON, mage.cards.u.UnclaimedTerritory.class)); cards.add(new SetCardInfo("Underworld Connections", 138, Rarity.RARE, mage.cards.u.UnderworldConnections.class)); cards.add(new SetCardInfo("Unstable Obelisk", 170, Rarity.UNCOMMON, mage.cards.u.UnstableObelisk.class)); @@ -171,7 +204,8 @@ public final class CrimsonVowCommander extends ExpansionSet { cards.add(new SetCardInfo("Vampiric Dragon", 158, Rarity.RARE, mage.cards.v.VampiricDragon.class)); cards.add(new SetCardInfo("Vandalblast", 152, Rarity.UNCOMMON, mage.cards.v.Vandalblast.class)); cards.add(new SetCardInfo("Verity Circle", 116, Rarity.RARE, mage.cards.v.VerityCircle.class)); - cards.add(new SetCardInfo("Wedding Ring", 32, Rarity.MYTHIC, mage.cards.w.WeddingRing.class)); + cards.add(new SetCardInfo("Wedding Ring", 32, Rarity.MYTHIC, mage.cards.w.WeddingRing.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Wedding Ring", 70, Rarity.MYTHIC, mage.cards.w.WeddingRing.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Windborn Muse", 101, Rarity.RARE, mage.cards.w.WindbornMuse.class)); } } diff --git a/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java b/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java index 90c4f3ffec5..ba6239f81f6 100644 --- a/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java +++ b/Mage.Sets/src/mage/sets/ForgottenRealmsCommander.java @@ -104,8 +104,10 @@ public final class ForgottenRealmsCommander extends ExpansionSet { cards.add(new SetCardInfo("Exotic Orchard", 236, Rarity.RARE, mage.cards.e.ExoticOrchard.class)); cards.add(new SetCardInfo("Explore", 157, Rarity.COMMON, mage.cards.e.Explore.class)); cards.add(new SetCardInfo("Explorer's Scope", 205, Rarity.COMMON, mage.cards.e.ExplorersScope.class)); + cards.add(new SetCardInfo("Extract Brain", 46, Rarity.RARE, mage.cards.e.ExtractBrain.class)); cards.add(new SetCardInfo("Fellwar Stone", 206, Rarity.UNCOMMON, mage.cards.f.FellwarStone.class)); cards.add(new SetCardInfo("Fertile Ground", 158, Rarity.COMMON, mage.cards.f.FertileGround.class)); + cards.add(new SetCardInfo("Fevered Suspicion", 47, Rarity.RARE, mage.cards.f.FeveredSuspicion.class)); cards.add(new SetCardInfo("Fey Steed", 5, Rarity.RARE, mage.cards.f.FeySteed.class)); cards.add(new SetCardInfo("Fiend of the Shadows", 99, Rarity.RARE, mage.cards.f.FiendOfTheShadows.class)); cards.add(new SetCardInfo("Fiendlash", 31, Rarity.RARE, mage.cards.f.Fiendlash.class)); @@ -217,6 +219,7 @@ public final class ForgottenRealmsCommander extends ExpansionSet { cards.add(new SetCardInfo("Rishkar's Expertise", 170, Rarity.RARE, mage.cards.r.RishkarsExpertise.class)); cards.add(new SetCardInfo("Riverwise Augur", 93, Rarity.UNCOMMON, mage.cards.r.RiverwiseAugur.class)); cards.add(new SetCardInfo("Robe of Stars", 11, Rarity.RARE, mage.cards.r.RobeOfStars.class)); + cards.add(new SetCardInfo("Rod of Absorption", 19, Rarity.RARE, mage.cards.r.RodOfAbsorption.class)); cards.add(new SetCardInfo("Ronom Unicorn", 71, Rarity.COMMON, mage.cards.r.RonomUnicorn.class)); cards.add(new SetCardInfo("Savage Ventmaw", 191, Rarity.UNCOMMON, mage.cards.s.SavageVentmaw.class)); cards.add(new SetCardInfo("Scourge of Valkas", 137, Rarity.RARE, mage.cards.s.ScourgeOfValkas.class)); diff --git a/Mage.Sets/src/mage/sets/KamigawaNeonDynasty.java b/Mage.Sets/src/mage/sets/KamigawaNeonDynasty.java index 199f453f314..1db9bd62ee3 100644 --- a/Mage.Sets/src/mage/sets/KamigawaNeonDynasty.java +++ b/Mage.Sets/src/mage/sets/KamigawaNeonDynasty.java @@ -24,18 +24,26 @@ public final class KamigawaNeonDynasty extends ExpansionSet { cards.add(new SetCardInfo("Acquisition Octopus", 44, Rarity.UNCOMMON, mage.cards.a.AcquisitionOctopus.class)); cards.add(new SetCardInfo("Akki Ember-Keeper", 130, Rarity.COMMON, mage.cards.a.AkkiEmberKeeper.class)); - cards.add(new SetCardInfo("Akki Ronin", 131, Rarity.COMMON, mage.cards.a.AkkiRonin.class)); + cards.add(new SetCardInfo("Akki Ronin", 131, Rarity.COMMON, mage.cards.a.AkkiRonin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Akki Ronin", 319, Rarity.COMMON, mage.cards.a.AkkiRonin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Akki War Paint", 132, Rarity.COMMON, mage.cards.a.AkkiWarPaint.class)); cards.add(new SetCardInfo("Ambitious Assault", 133, Rarity.COMMON, mage.cards.a.AmbitiousAssault.class)); cards.add(new SetCardInfo("Ancestral Katana", 1, Rarity.COMMON, mage.cards.a.AncestralKatana.class)); cards.add(new SetCardInfo("Anchor to Reality", 45, Rarity.UNCOMMON, mage.cards.a.AnchorToReality.class)); cards.add(new SetCardInfo("Animus of Night's Reach", 109, Rarity.UNCOMMON, mage.cards.a.AnimusOfNightsReach.class)); - cards.add(new SetCardInfo("Ao, the Dawn Sky", 2, Rarity.MYTHIC, mage.cards.a.AoTheDawnSky.class)); - cards.add(new SetCardInfo("Architect of Restoration", 34, Rarity.RARE, mage.cards.a.ArchitectOfRestoration.class)); + cards.add(new SetCardInfo("Ao, the Dawn Sky", 2, Rarity.MYTHIC, mage.cards.a.AoTheDawnSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ao, the Dawn Sky", 406, Rarity.MYTHIC, mage.cards.a.AoTheDawnSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ao, the Dawn Sky", 433, Rarity.MYTHIC, mage.cards.a.AoTheDawnSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Architect of Restoration", 34, Rarity.RARE, mage.cards.a.ArchitectOfRestoration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Architect of Restoration", 354, Rarity.RARE, mage.cards.a.ArchitectOfRestoration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Architect of Restoration", 442, Rarity.RARE, mage.cards.a.ArchitectOfRestoration.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Armguard Familiar", 46, Rarity.COMMON, mage.cards.a.ArmguardFamiliar.class)); - cards.add(new SetCardInfo("Asari Captain", 215, Rarity.UNCOMMON, mage.cards.a.AsariCaptain.class)); + cards.add(new SetCardInfo("Asari Captain", 215, Rarity.UNCOMMON, mage.cards.a.AsariCaptain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Asari Captain", 327, Rarity.UNCOMMON, mage.cards.a.AsariCaptain.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Assassin's Ink", 87, Rarity.UNCOMMON, mage.cards.a.AssassinsInk.class)); - cards.add(new SetCardInfo("Atsushi, the Blazing Sky", 134, Rarity.MYTHIC, mage.cards.a.AtsushiTheBlazingSky.class)); + cards.add(new SetCardInfo("Atsushi, the Blazing Sky", 134, Rarity.MYTHIC, mage.cards.a.AtsushiTheBlazingSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Atsushi, the Blazing Sky", 410, Rarity.MYTHIC, mage.cards.a.AtsushiTheBlazingSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Atsushi, the Blazing Sky", 463, Rarity.MYTHIC, mage.cards.a.AtsushiTheBlazingSky.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Automated Artificer", 239, Rarity.COMMON, mage.cards.a.AutomatedArtificer.class)); cards.add(new SetCardInfo("Awakened Awareness", 47, Rarity.UNCOMMON, mage.cards.a.AwakenedAwareness.class)); cards.add(new SetCardInfo("Azusa's Many Journeys", 172, Rarity.UNCOMMON, mage.cards.a.AzusasManyJourneys.class)); @@ -44,18 +52,28 @@ public final class KamigawaNeonDynasty extends ExpansionSet { cards.add(new SetCardInfo("Bearer of Memory", 174, Rarity.COMMON, mage.cards.b.BearerOfMemory.class)); cards.add(new SetCardInfo("Befriending the Moths", 4, Rarity.COMMON, mage.cards.b.BefriendingTheMoths.class)); cards.add(new SetCardInfo("Behold the Unspeakable", 48, Rarity.UNCOMMON, mage.cards.b.BeholdTheUnspeakable.class)); - cards.add(new SetCardInfo("Biting-Palm Ninja", 88, Rarity.RARE, mage.cards.b.BitingPalmNinja.class)); - cards.add(new SetCardInfo("Blade of the Oni", 89, Rarity.MYTHIC, mage.cards.b.BladeOfTheOni.class)); - cards.add(new SetCardInfo("Blade-Blizzard Kitsune", 5, Rarity.UNCOMMON, mage.cards.b.BladeBlizzardKitsune.class)); + cards.add(new SetCardInfo("Biting-Palm Ninja", 338, Rarity.RARE, mage.cards.b.BitingPalmNinja.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Biting-Palm Ninja", 452, Rarity.RARE, mage.cards.b.BitingPalmNinja.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Biting-Palm Ninja", 88, Rarity.RARE, mage.cards.b.BitingPalmNinja.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blade of the Oni", 377, Rarity.MYTHIC, mage.cards.b.BladeOfTheOni.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blade of the Oni", 420, Rarity.MYTHIC, mage.cards.b.BladeOfTheOni.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blade of the Oni", 453, Rarity.MYTHIC, mage.cards.b.BladeOfTheOni.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blade of the Oni", 89, Rarity.MYTHIC, mage.cards.b.BladeOfTheOni.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blade-Blizzard Kitsune", 331, Rarity.UNCOMMON, mage.cards.b.BladeBlizzardKitsune.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Blade-Blizzard Kitsune", 5, Rarity.UNCOMMON, mage.cards.b.BladeBlizzardKitsune.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bloodfell Caves", 264, Rarity.COMMON, mage.cards.b.BloodfellCaves.class)); cards.add(new SetCardInfo("Blossom Prancer", 175, Rarity.UNCOMMON, mage.cards.b.BlossomPrancer.class)); cards.add(new SetCardInfo("Blossoming Sands", 265, Rarity.COMMON, mage.cards.b.BlossomingSands.class)); cards.add(new SetCardInfo("Boon of Boseiju", 176, Rarity.UNCOMMON, mage.cards.b.BoonOfBoseiju.class)); cards.add(new SetCardInfo("Born to Drive", 6, Rarity.UNCOMMON, mage.cards.b.BornToDrive.class)); cards.add(new SetCardInfo("Boseiju Reaches Skyward", 177, Rarity.UNCOMMON, mage.cards.b.BoseijuReachesSkyward.class)); - cards.add(new SetCardInfo("Boseiju, Who Endures", 266, Rarity.RARE, mage.cards.b.BoseijuWhoEndures.class)); + cards.add(new SetCardInfo("Boseiju, Who Endures", 266, Rarity.RARE, mage.cards.b.BoseijuWhoEndures.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Boseiju, Who Endures", 412, Rarity.RARE, mage.cards.b.BoseijuWhoEndures.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Boseiju, Who Endures", 501, Rarity.RARE, mage.cards.b.BoseijuWhoEndures.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Branch of Boseiju", 177, Rarity.UNCOMMON, mage.cards.b.BranchOfBoseiju.class)); - cards.add(new SetCardInfo("Brilliant Restoration", 7, Rarity.RARE, mage.cards.b.BrilliantRestoration.class)); + cards.add(new SetCardInfo("Brilliant Restoration", 363, Rarity.RARE, mage.cards.b.BrilliantRestoration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Brilliant Restoration", 434, Rarity.RARE, mage.cards.b.BrilliantRestoration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Brilliant Restoration", 7, Rarity.RARE, mage.cards.b.BrilliantRestoration.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Bronze Cudgels", 240, Rarity.UNCOMMON, mage.cards.b.BronzeCudgels.class)); cards.add(new SetCardInfo("Bronzeplate Boar", 135, Rarity.UNCOMMON, mage.cards.b.BronzeplateBoar.class)); cards.add(new SetCardInfo("Brute Suit", 241, Rarity.COMMON, mage.cards.b.BruteSuit.class)); @@ -63,45 +81,78 @@ public final class KamigawaNeonDynasty extends ExpansionSet { cards.add(new SetCardInfo("Chainflail Centipede", 90, Rarity.COMMON, mage.cards.c.ChainflailCentipede.class)); cards.add(new SetCardInfo("Circuit Mender", 242, Rarity.UNCOMMON, mage.cards.c.CircuitMender.class)); cards.add(new SetCardInfo("Clawing Torment", 91, Rarity.COMMON, mage.cards.c.ClawingTorment.class)); - cards.add(new SetCardInfo("Cloudsteel Kirin", 8, Rarity.RARE, mage.cards.c.CloudsteelKirin.class)); - cards.add(new SetCardInfo("Coiling Stalker", 179, Rarity.COMMON, mage.cards.c.CoilingStalker.class)); + cards.add(new SetCardInfo("Cloudsteel Kirin", 364, Rarity.RARE, mage.cards.c.CloudsteelKirin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cloudsteel Kirin", 435, Rarity.RARE, mage.cards.c.CloudsteelKirin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Cloudsteel Kirin", 8, Rarity.RARE, mage.cards.c.CloudsteelKirin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Coiling Stalker", 179, Rarity.COMMON, mage.cards.c.CoilingStalker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Coiling Stalker", 346, Rarity.COMMON, mage.cards.c.CoilingStalker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Colossal Skyturtle", 216, Rarity.UNCOMMON, mage.cards.c.ColossalSkyturtle.class)); cards.add(new SetCardInfo("Commune with Spirits", 180, Rarity.COMMON, mage.cards.c.CommuneWithSpirits.class)); cards.add(new SetCardInfo("Containment Construct", 243, Rarity.UNCOMMON, mage.cards.c.ContainmentConstruct.class)); - cards.add(new SetCardInfo("Covert Technician", 49, Rarity.UNCOMMON, mage.cards.c.CovertTechnician.class)); + cards.add(new SetCardInfo("Covert Technician", 332, Rarity.UNCOMMON, mage.cards.c.CovertTechnician.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Covert Technician", 49, Rarity.UNCOMMON, mage.cards.c.CovertTechnician.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Crackling Emergence", 136, Rarity.COMMON, mage.cards.c.CracklingEmergence.class)); cards.add(new SetCardInfo("Debt to the Kami", 92, Rarity.COMMON, mage.cards.d.DebtToTheKami.class)); + cards.add(new SetCardInfo("Discover the Impossible", 50, Rarity.UNCOMMON, mage.cards.d.DiscoverTheImpossible.class)); cards.add(new SetCardInfo("Dismal Backwater", 267, Rarity.COMMON, mage.cards.d.DismalBackwater.class)); cards.add(new SetCardInfo("Disruption Protocol", 51, Rarity.COMMON, mage.cards.d.DisruptionProtocol.class)); cards.add(new SetCardInfo("Dockside Chef", 93, Rarity.UNCOMMON, mage.cards.d.DocksideChef.class)); - cards.add(new SetCardInfo("Dokuchi Shadow-Walker", 94, Rarity.COMMON, mage.cards.d.DokuchiShadowWalker.class)); - cards.add(new SetCardInfo("Dokuchi Silencer", 95, Rarity.UNCOMMON, mage.cards.d.DokuchiSilencer.class)); + cards.add(new SetCardInfo("Dokuchi Shadow-Walker", 339, Rarity.COMMON, mage.cards.d.DokuchiShadowWalker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dokuchi Shadow-Walker", 94, Rarity.COMMON, mage.cards.d.DokuchiShadowWalker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dokuchi Silencer", 340, Rarity.UNCOMMON, mage.cards.d.DokuchiSilencer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dokuchi Silencer", 95, Rarity.UNCOMMON, mage.cards.d.DokuchiSilencer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragon-Kami's Egg", 181, Rarity.RARE, mage.cards.d.DragonKamisEgg.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragon-Kami's Egg", 358, Rarity.RARE, mage.cards.d.DragonKamisEgg.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Dragon-Kami's Egg", 473, Rarity.RARE, mage.cards.d.DragonKamisEgg.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Dragonfly Suit", 9, Rarity.COMMON, mage.cards.d.DragonflySuit.class)); cards.add(new SetCardInfo("Dragonspark Reactor", 137, Rarity.UNCOMMON, mage.cards.d.DragonsparkReactor.class)); cards.add(new SetCardInfo("Dramatist's Puppet", 244, Rarity.COMMON, mage.cards.d.DramatistsPuppet.class)); - cards.add(new SetCardInfo("Eater of Virtue", 245, Rarity.RARE, mage.cards.e.EaterOfVirtue.class)); - cards.add(new SetCardInfo("Echo of Death's Wail", 124, Rarity.RARE, mage.cards.e.EchoOfDeathsWail.class)); + cards.add(new SetCardInfo("Eater of Virtue", 245, Rarity.RARE, mage.cards.e.EaterOfVirtue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eater of Virtue", 401, Rarity.RARE, mage.cards.e.EaterOfVirtue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eater of Virtue", 496, Rarity.RARE, mage.cards.e.EaterOfVirtue.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Echo of Death's Wail", 124, Rarity.RARE, mage.cards.e.EchoOfDeathsWail.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Echo of Death's Wail", 356, Rarity.RARE, mage.cards.e.EchoOfDeathsWail.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Echo of Death's Wail", 462, Rarity.RARE, mage.cards.e.EchoOfDeathsWail.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Ecologist's Terrarium", 246, Rarity.COMMON, mage.cards.e.EcologistsTerrarium.class)); - cards.add(new SetCardInfo("Eiganjo Exemplar", 10, Rarity.COMMON, mage.cards.e.EiganjoExemplar.class)); - cards.add(new SetCardInfo("Eiganjo Uprising", 217, Rarity.RARE, mage.cards.e.EiganjoUprising.class)); - cards.add(new SetCardInfo("Eiganjo, Seat of the Empire", 268, Rarity.RARE, mage.cards.e.EiganjoSeatOfTheEmpire.class)); + cards.add(new SetCardInfo("Eiganjo Exemplar", 10, Rarity.COMMON, mage.cards.e.EiganjoExemplar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eiganjo Exemplar", 309, Rarity.COMMON, mage.cards.e.EiganjoExemplar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eiganjo Uprising", 217, Rarity.RARE, mage.cards.e.EiganjoUprising.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eiganjo Uprising", 396, Rarity.RARE, mage.cards.e.EiganjoUprising.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eiganjo Uprising", 484, Rarity.RARE, mage.cards.e.EiganjoUprising.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eiganjo, Seat of the Empire", 268, Rarity.RARE, mage.cards.e.EiganjoSeatOfTheEmpire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eiganjo, Seat of the Empire", 413, Rarity.RARE, mage.cards.e.EiganjoSeatOfTheEmpire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Eiganjo, Seat of the Empire", 502, Rarity.RARE, mage.cards.e.EiganjoSeatOfTheEmpire.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Enormous Energy Blade", 96, Rarity.UNCOMMON, mage.cards.e.EnormousEnergyBlade.class)); - cards.add(new SetCardInfo("Enthusiastic Mechanaut", 218, Rarity.UNCOMMON, mage.cards.e.EnthusiasticMechanaut.class)); + cards.add(new SetCardInfo("Enthusiastic Mechanaut", 218, Rarity.UNCOMMON, mage.cards.e.EnthusiasticMechanaut.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Enthusiastic Mechanaut", 509, Rarity.UNCOMMON, mage.cards.e.EnthusiasticMechanaut.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Era of Enlightenment", 11, Rarity.COMMON, mage.cards.e.EraOfEnlightenment.class)); cards.add(new SetCardInfo("Essence Capture", 52, Rarity.UNCOMMON, mage.cards.e.EssenceCapture.class)); cards.add(new SetCardInfo("Etching of Kumano", 152, Rarity.UNCOMMON, mage.cards.e.EtchingOfKumano.class)); cards.add(new SetCardInfo("Experimental Synthesizer", 138, Rarity.COMMON, mage.cards.e.ExperimentalSynthesizer.class)); cards.add(new SetCardInfo("Explosive Entry", 139, Rarity.COMMON, mage.cards.e.ExplosiveEntry.class)); - cards.add(new SetCardInfo("Explosive Singularity", 140, Rarity.MYTHIC, mage.cards.e.ExplosiveSingularity.class)); - cards.add(new SetCardInfo("Fable of the Mirror-Breaker", 141, Rarity.RARE, mage.cards.f.FableOfTheMirrorBreaker.class)); + cards.add(new SetCardInfo("Explosive Singularity", 140, Rarity.MYTHIC, mage.cards.e.ExplosiveSingularity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Explosive Singularity", 383, Rarity.MYTHIC, mage.cards.e.ExplosiveSingularity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Explosive Singularity", 422, Rarity.MYTHIC, mage.cards.e.ExplosiveSingularity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Explosive Singularity", 464, Rarity.MYTHIC, mage.cards.e.ExplosiveSingularity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fable of the Mirror-Breaker", 141, Rarity.RARE, mage.cards.f.FableOfTheMirrorBreaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fable of the Mirror-Breaker", 357, Rarity.RARE, mage.cards.f.FableOfTheMirrorBreaker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fable of the Mirror-Breaker", 465, Rarity.RARE, mage.cards.f.FableOfTheMirrorBreaker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Fade into Antiquity", 182, Rarity.COMMON, mage.cards.f.FadeIntoAntiquity.class)); - cards.add(new SetCardInfo("Fang of Shigeki", 183, Rarity.COMMON, mage.cards.f.FangOfShigeki.class)); - cards.add(new SetCardInfo("Farewell", 13, Rarity.RARE, mage.cards.f.Farewell.class)); + cards.add(new SetCardInfo("Fang of Shigeki", 183, Rarity.COMMON, mage.cards.f.FangOfShigeki.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Fang of Shigeki", 347, Rarity.COMMON, mage.cards.f.FangOfShigeki.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Farewell", 13, Rarity.RARE, mage.cards.f.Farewell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Farewell", 365, Rarity.RARE, mage.cards.f.Farewell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Farewell", 417, Rarity.RARE, mage.cards.f.Farewell.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Farewell", 436, Rarity.RARE, mage.cards.f.Farewell.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Favor of Jukai", 184, Rarity.COMMON, mage.cards.f.FavorOfJukai.class)); cards.add(new SetCardInfo("Flame Discharge", 142, Rarity.UNCOMMON, mage.cards.f.FlameDischarge.class)); cards.add(new SetCardInfo("Forest", 291, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 292, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Forest", 301, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Forest", 302, Rarity.LAND, mage.cards.basiclands.Forest.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Fragment of Konda", 12, Rarity.UNCOMMON, mage.cards.f.FragmentOfKonda.class)); - cards.add(new SetCardInfo("Futurist Operative", 53, Rarity.UNCOMMON, mage.cards.f.FuturistOperative.class)); + cards.add(new SetCardInfo("Futurist Operative", 333, Rarity.UNCOMMON, mage.cards.f.FuturistOperative.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Futurist Operative", 53, Rarity.UNCOMMON, mage.cards.f.FuturistOperative.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Futurist Sentinel", 54, Rarity.COMMON, mage.cards.f.FuturistSentinel.class)); cards.add(new SetCardInfo("Generous Visitor", 185, Rarity.UNCOMMON, mage.cards.g.GenerousVisitor.class)); cards.add(new SetCardInfo("Geothermal Kami", 186, Rarity.COMMON, mage.cards.g.GeothermalKami.class)); @@ -113,217 +164,413 @@ public final class KamigawaNeonDynasty extends ExpansionSet { cards.add(new SetCardInfo("Go-Shintai of Lost Wisdom", 55, Rarity.UNCOMMON, mage.cards.g.GoShintaiOfLostWisdom.class)); cards.add(new SetCardInfo("Go-Shintai of Shared Purpose", 14, Rarity.UNCOMMON, mage.cards.g.GoShintaiOfSharedPurpose.class)); cards.add(new SetCardInfo("Golden-Tail Disciple", 15, Rarity.COMMON, mage.cards.g.GoldenTailDisciple.class)); - cards.add(new SetCardInfo("Goro-Goro, Disciple of Ryusei", 145, Rarity.RARE, mage.cards.g.GoroGoroDiscipleOfRyusei.class)); + cards.add(new SetCardInfo("Goro-Goro, Disciple of Ryusei", 145, Rarity.RARE, mage.cards.g.GoroGoroDiscipleOfRyusei.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Goro-Goro, Disciple of Ryusei", 320, Rarity.RARE, mage.cards.g.GoroGoroDiscipleOfRyusei.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Goro-Goro, Disciple of Ryusei", 466, Rarity.RARE, mage.cards.g.GoroGoroDiscipleOfRyusei.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Grafted Growth", 188, Rarity.COMMON, mage.cards.g.GraftedGrowth.class)); cards.add(new SetCardInfo("Gravelighter", 98, Rarity.UNCOMMON, mage.cards.g.Gravelighter.class)); - cards.add(new SetCardInfo("Greasefang, Okiba Boss", 220, Rarity.RARE, mage.cards.g.GreasefangOkibaBoss.class)); + cards.add(new SetCardInfo("Greasefang, Okiba Boss", 220, Rarity.RARE, mage.cards.g.GreasefangOkibaBoss.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Greasefang, Okiba Boss", 397, Rarity.RARE, mage.cards.g.GreasefangOkibaBoss.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Greasefang, Okiba Boss", 485, Rarity.RARE, mage.cards.g.GreasefangOkibaBoss.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Greater Tanuki", 189, Rarity.COMMON, mage.cards.g.GreaterTanuki.class)); - cards.add(new SetCardInfo("Guardians of Oboro", 56, Rarity.COMMON, mage.cards.g.GuardiansOfOboro.class)); + cards.add(new SetCardInfo("Guardians of Oboro", 317, Rarity.COMMON, mage.cards.g.GuardiansOfOboro.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Guardians of Oboro", 56, Rarity.COMMON, mage.cards.g.GuardiansOfOboro.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Hand of Enlightenment", 11, Rarity.COMMON, mage.cards.h.HandOfEnlightenment.class)); cards.add(new SetCardInfo("Harmonious Emergence", 190, Rarity.COMMON, mage.cards.h.HarmoniousEmergence.class)); - cards.add(new SetCardInfo("Heiko Yamazaki, the General", 146, Rarity.UNCOMMON, mage.cards.h.HeikoYamazakiTheGeneral.class)); - cards.add(new SetCardInfo("Heir of the Ancient Fang", 191, Rarity.COMMON, mage.cards.h.HeirOfTheAncientFang.class)); - cards.add(new SetCardInfo("Hidetsugu Consumes All", 221, Rarity.MYTHIC, mage.cards.h.HidetsuguConsumesAll.class)); - cards.add(new SetCardInfo("Hidetsugu, Devouring Chaos", 99, Rarity.RARE, mage.cards.h.HidetsuguDevouringChaos.class)); + cards.add(new SetCardInfo("Heiko Yamazaki, the General", 146, Rarity.UNCOMMON, mage.cards.h.HeikoYamazakiTheGeneral.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Heiko Yamazaki, the General", 321, Rarity.UNCOMMON, mage.cards.h.HeikoYamazakiTheGeneral.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Heir of the Ancient Fang", 191, Rarity.COMMON, mage.cards.h.HeirOfTheAncientFang.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Heir of the Ancient Fang", 325, Rarity.COMMON, mage.cards.h.HeirOfTheAncientFang.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hidetsugu Consumes All", 221, Rarity.MYTHIC, mage.cards.h.HidetsuguConsumesAll.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hidetsugu Consumes All", 361, Rarity.MYTHIC, mage.cards.h.HidetsuguConsumesAll.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hidetsugu Consumes All", 486, Rarity.MYTHIC, mage.cards.h.HidetsuguConsumesAll.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hidetsugu, Devouring Chaos", 378, Rarity.RARE, mage.cards.h.HidetsuguDevouringChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hidetsugu, Devouring Chaos", 429, Rarity.RARE, mage.cards.h.HidetsuguDevouringChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hidetsugu, Devouring Chaos", 430, Rarity.RARE, mage.cards.h.HidetsuguDevouringChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hidetsugu, Devouring Chaos", 431, Rarity.RARE, mage.cards.h.HidetsuguDevouringChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hidetsugu, Devouring Chaos", 432, Rarity.RARE, mage.cards.h.HidetsuguDevouringChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hidetsugu, Devouring Chaos", 454, Rarity.RARE, mage.cards.h.HidetsuguDevouringChaos.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hidetsugu, Devouring Chaos", 99, Rarity.RARE, mage.cards.h.HidetsuguDevouringChaos.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("High-Speed Hoverbike", 247, Rarity.UNCOMMON, mage.cards.h.HighSpeedHoverbike.class)); - cards.add(new SetCardInfo("Hinata, Dawn-Crowned", 222, Rarity.RARE, mage.cards.h.HinataDawnCrowned.class)); + cards.add(new SetCardInfo("Hinata, Dawn-Crowned", 222, Rarity.RARE, mage.cards.h.HinataDawnCrowned.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hinata, Dawn-Crowned", 398, Rarity.RARE, mage.cards.h.HinataDawnCrowned.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Hinata, Dawn-Crowned", 487, Rarity.RARE, mage.cards.h.HinataDawnCrowned.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Historian's Wisdom", 192, Rarity.UNCOMMON, mage.cards.h.HistoriansWisdom.class)); cards.add(new SetCardInfo("Hotshot Mechanic", 16, Rarity.UNCOMMON, mage.cards.h.HotshotMechanic.class)); cards.add(new SetCardInfo("Imperial Moth", 4, Rarity.COMMON, mage.cards.i.ImperialMoth.class)); cards.add(new SetCardInfo("Imperial Oath", 17, Rarity.COMMON, mage.cards.i.ImperialOath.class)); cards.add(new SetCardInfo("Imperial Recovery Unit", 18, Rarity.UNCOMMON, mage.cards.i.ImperialRecoveryUnit.class)); - cards.add(new SetCardInfo("Imperial Subduer", 19, Rarity.COMMON, mage.cards.i.ImperialSubduer.class)); - cards.add(new SetCardInfo("Inkrise Infiltrator", 100, Rarity.COMMON, mage.cards.i.InkriseInfiltrator.class)); + cards.add(new SetCardInfo("Imperial Subduer", 19, Rarity.COMMON, mage.cards.i.ImperialSubduer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Imperial Subduer", 310, Rarity.COMMON, mage.cards.i.ImperialSubduer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inkrise Infiltrator", 100, Rarity.COMMON, mage.cards.i.InkriseInfiltrator.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inkrise Infiltrator", 341, Rarity.COMMON, mage.cards.i.InkriseInfiltrator.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Intercessor's Arrest", 20, Rarity.COMMON, mage.cards.i.IntercessorsArrest.class)); - cards.add(new SetCardInfo("Inventive Iteration", 57, Rarity.RARE, mage.cards.i.InventiveIteration.class)); + cards.add(new SetCardInfo("Inventive Iteration", 355, Rarity.RARE, mage.cards.i.InventiveIteration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inventive Iteration", 443, Rarity.RARE, mage.cards.i.InventiveIteration.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Inventive Iteration", 57, Rarity.RARE, mage.cards.i.InventiveIteration.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Invigorating Hot Spring", 223, Rarity.UNCOMMON, mage.cards.i.InvigoratingHotSpring.class)); - cards.add(new SetCardInfo("Invoke Despair", 101, Rarity.RARE, mage.cards.i.InvokeDespair.class)); - cards.add(new SetCardInfo("Invoke Justice", 21, Rarity.RARE, mage.cards.i.InvokeJustice.class)); - cards.add(new SetCardInfo("Invoke the Ancients", 193, Rarity.RARE, mage.cards.i.InvokeTheAncients.class)); - cards.add(new SetCardInfo("Invoke the Winds", 58, Rarity.RARE, mage.cards.i.InvokeTheWinds.class)); + cards.add(new SetCardInfo("Invoke Calamity", 147, Rarity.RARE, mage.cards.i.InvokeCalamity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke Calamity", 384, Rarity.RARE, mage.cards.i.InvokeCalamity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke Calamity", 467, Rarity.RARE, mage.cards.i.InvokeCalamity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke Despair", 101, Rarity.RARE, mage.cards.i.InvokeDespair.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke Despair", 379, Rarity.RARE, mage.cards.i.InvokeDespair.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke Despair", 455, Rarity.RARE, mage.cards.i.InvokeDespair.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke Despair", 506, Rarity.RARE, mage.cards.i.InvokeDespair.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke Justice", 21, Rarity.RARE, mage.cards.i.InvokeJustice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke Justice", 366, Rarity.RARE, mage.cards.i.InvokeJustice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke Justice", 437, Rarity.RARE, mage.cards.i.InvokeJustice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke the Ancients", 193, Rarity.RARE, mage.cards.i.InvokeTheAncients.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke the Ancients", 390, Rarity.RARE, mage.cards.i.InvokeTheAncients.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke the Ancients", 474, Rarity.RARE, mage.cards.i.InvokeTheAncients.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke the Winds", 370, Rarity.RARE, mage.cards.i.InvokeTheWinds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke the Winds", 444, Rarity.RARE, mage.cards.i.InvokeTheWinds.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Invoke the Winds", 58, Rarity.RARE, mage.cards.i.InvokeTheWinds.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Iron Apprentice", 248, Rarity.COMMON, mage.cards.i.IronApprentice.class)); cards.add(new SetCardInfo("Ironhoof Boar", 148, Rarity.COMMON, mage.cards.i.IronhoofBoar.class)); cards.add(new SetCardInfo("Island", 285, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Isshin, Two Heavens as One", 224, Rarity.RARE, mage.cards.i.IsshinTwoHeavensAsOne.class)); - cards.add(new SetCardInfo("Jin-Gitaxias, Progress Tyrant", 59, Rarity.MYTHIC, mage.cards.j.JinGitaxiasProgressTyrant.class)); - cards.add(new SetCardInfo("Jugan Defends the Temple", 194, Rarity.MYTHIC, mage.cards.j.JuganDefendsTheTemple.class)); - cards.add(new SetCardInfo("Jukai Naturalist", 225, Rarity.UNCOMMON, mage.cards.j.JukaiNaturalist.class)); + cards.add(new SetCardInfo("Island", 286, Rarity.LAND, mage.cards.basiclands.Island.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Island", 295, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Island", 296, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Isshin, Two Heavens as One", 224, Rarity.RARE, mage.cards.i.IsshinTwoHeavensAsOne.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Isshin, Two Heavens as One", 328, Rarity.RARE, mage.cards.i.IsshinTwoHeavensAsOne.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Isshin, Two Heavens as One", 488, Rarity.RARE, mage.cards.i.IsshinTwoHeavensAsOne.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jin-Gitaxias, Progress Tyrant", 307, Rarity.MYTHIC, mage.cards.j.JinGitaxiasProgressTyrant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jin-Gitaxias, Progress Tyrant", 371, Rarity.MYTHIC, mage.cards.j.JinGitaxiasProgressTyrant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jin-Gitaxias, Progress Tyrant", 427, Rarity.MYTHIC, mage.cards.j.JinGitaxiasProgressTyrant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jin-Gitaxias, Progress Tyrant", 445, Rarity.MYTHIC, mage.cards.j.JinGitaxiasProgressTyrant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jin-Gitaxias, Progress Tyrant", 59, Rarity.MYTHIC, mage.cards.j.JinGitaxiasProgressTyrant.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jugan Defends the Temple", 194, Rarity.MYTHIC, mage.cards.j.JuganDefendsTheTemple.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jugan Defends the Temple", 359, Rarity.MYTHIC, mage.cards.j.JuganDefendsTheTemple.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jugan Defends the Temple", 475, Rarity.MYTHIC, mage.cards.j.JuganDefendsTheTemple.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jukai Naturalist", 225, Rarity.UNCOMMON, mage.cards.j.JukaiNaturalist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jukai Naturalist", 510, Rarity.UNCOMMON, mage.cards.j.JukaiNaturalist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jukai Preserver", 195, Rarity.COMMON, mage.cards.j.JukaiPreserver.class)); - cards.add(new SetCardInfo("Jukai Trainee", 196, Rarity.COMMON, mage.cards.j.JukaiTrainee.class)); + cards.add(new SetCardInfo("Jukai Trainee", 196, Rarity.COMMON, mage.cards.j.JukaiTrainee.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Jukai Trainee", 326, Rarity.COMMON, mage.cards.j.JukaiTrainee.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Jungle Hollow", 269, Rarity.COMMON, mage.cards.j.JungleHollow.class)); - cards.add(new SetCardInfo("Junji, the Midnight Sky", 102, Rarity.MYTHIC, mage.cards.j.JunjiTheMidnightSky.class)); - cards.add(new SetCardInfo("Kairi, the Swirling Sky", 60, Rarity.MYTHIC, mage.cards.k.KairiTheSwirlingSky.class)); - cards.add(new SetCardInfo("Kaito Shizuki", 226, Rarity.MYTHIC, mage.cards.k.KaitoShizuki.class)); + cards.add(new SetCardInfo("Junji, the Midnight Sky", 102, Rarity.MYTHIC, mage.cards.j.JunjiTheMidnightSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Junji, the Midnight Sky", 409, Rarity.MYTHIC, mage.cards.j.JunjiTheMidnightSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Junji, the Midnight Sky", 456, Rarity.MYTHIC, mage.cards.j.JunjiTheMidnightSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kairi, the Swirling Sky", 408, Rarity.MYTHIC, mage.cards.k.KairiTheSwirlingSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kairi, the Swirling Sky", 446, Rarity.MYTHIC, mage.cards.k.KairiTheSwirlingSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kairi, the Swirling Sky", 60, Rarity.MYTHIC, mage.cards.k.KairiTheSwirlingSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kaito Shizuki", 226, Rarity.MYTHIC, mage.cards.k.KaitoShizuki.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kaito Shizuki", 305, Rarity.MYTHIC, mage.cards.k.KaitoShizuki.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kaito Shizuki", 350, Rarity.MYTHIC, mage.cards.k.KaitoShizuki.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kaito Shizuki", 424, Rarity.MYTHIC, mage.cards.k.KaitoShizuki.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kaito's Pursuit", 103, Rarity.COMMON, mage.cards.k.KaitosPursuit.class)); cards.add(new SetCardInfo("Kami of Industry", 149, Rarity.COMMON, mage.cards.k.KamiOfIndustry.class)); cards.add(new SetCardInfo("Kami of Restless Shadows", 104, Rarity.COMMON, mage.cards.k.KamiOfRestlessShadows.class)); cards.add(new SetCardInfo("Kami of Terrible Secrets", 105, Rarity.COMMON, mage.cards.k.KamiOfTerribleSecrets.class)); - cards.add(new SetCardInfo("Kami of Transience", 197, Rarity.RARE, mage.cards.k.KamiOfTransience.class)); + cards.add(new SetCardInfo("Kami of Transience", 197, Rarity.RARE, mage.cards.k.KamiOfTransience.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kami of Transience", 391, Rarity.RARE, mage.cards.k.KamiOfTransience.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kami of Transience", 476, Rarity.RARE, mage.cards.k.KamiOfTransience.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kami's Flare", 150, Rarity.COMMON, mage.cards.k.KamisFlare.class)); - cards.add(new SetCardInfo("Kappa Tech-Wrecker", 198, Rarity.UNCOMMON, mage.cards.k.KappaTechWrecker.class)); + cards.add(new SetCardInfo("Kappa Tech-Wrecker", 198, Rarity.UNCOMMON, mage.cards.k.KappaTechWrecker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kappa Tech-Wrecker", 348, Rarity.UNCOMMON, mage.cards.k.KappaTechWrecker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kindled Fury", 151, Rarity.COMMON, mage.cards.k.KindledFury.class)); - cards.add(new SetCardInfo("Kirin-Touched Orochi", 212, Rarity.RARE, mage.cards.k.KirinTouchedOrochi.class)); + cards.add(new SetCardInfo("Kirin-Touched Orochi", 212, Rarity.RARE, mage.cards.k.KirinTouchedOrochi.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kirin-Touched Orochi", 360, Rarity.RARE, mage.cards.k.KirinTouchedOrochi.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kirin-Touched Orochi", 482, Rarity.RARE, mage.cards.k.KirinTouchedOrochi.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kitsune Ace", 22, Rarity.COMMON, mage.cards.k.KitsuneAce.class)); - cards.add(new SetCardInfo("Kodama of the West Tree", 199, Rarity.MYTHIC, mage.cards.k.KodamaOfTheWestTree.class)); - cards.add(new SetCardInfo("Kotose, the Silent Spider", 351, Rarity.RARE, mage.cards.k.KotoseTheSilentSpider.class)); + cards.add(new SetCardInfo("Kodama of the West Tree", 199, Rarity.MYTHIC, mage.cards.k.KodamaOfTheWestTree.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kodama of the West Tree", 392, Rarity.MYTHIC, mage.cards.k.KodamaOfTheWestTree.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kodama of the West Tree", 423, Rarity.MYTHIC, mage.cards.k.KodamaOfTheWestTree.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kodama of the West Tree", 477, Rarity.MYTHIC, mage.cards.k.KodamaOfTheWestTree.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kotose, the Silent Spider", 228, Rarity.RARE, mage.cards.k.KotoseTheSilentSpider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kotose, the Silent Spider", 351, Rarity.RARE, mage.cards.k.KotoseTheSilentSpider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kotose, the Silent Spider", 490, Rarity.RARE, mage.cards.k.KotoseTheSilentSpider.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Kumano Faces Kakkazan", 152, Rarity.UNCOMMON, mage.cards.k.KumanoFacesKakkazan.class)); - cards.add(new SetCardInfo("Kura, the Boundless Sky", 200, Rarity.MYTHIC, mage.cards.k.KuraTheBoundlessSky.class)); - cards.add(new SetCardInfo("Kyodai, Soul of Kamigawa", 23, Rarity.RARE, mage.cards.k.KyodaiSoulOfKamigawa.class)); + cards.add(new SetCardInfo("Kura, the Boundless Sky", 200, Rarity.MYTHIC, mage.cards.k.KuraTheBoundlessSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kura, the Boundless Sky", 411, Rarity.MYTHIC, mage.cards.k.KuraTheBoundlessSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kura, the Boundless Sky", 478, Rarity.MYTHIC, mage.cards.k.KuraTheBoundlessSky.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kyodai, Soul of Kamigawa", 23, Rarity.RARE, mage.cards.k.KyodaiSoulOfKamigawa.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kyodai, Soul of Kamigawa", 407, Rarity.RARE, mage.cards.k.KyodaiSoulOfKamigawa.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Kyodai, Soul of Kamigawa", 438, Rarity.RARE, mage.cards.k.KyodaiSoulOfKamigawa.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Leech Gauntlet", 106, Rarity.UNCOMMON, mage.cards.l.LeechGauntlet.class)); cards.add(new SetCardInfo("Lethal Exploit", 107, Rarity.COMMON, mage.cards.l.LethalExploit.class)); cards.add(new SetCardInfo("Life of Toshiro Umezawa", 108, Rarity.UNCOMMON, mage.cards.l.LifeOfToshiroUmezawa.class)); cards.add(new SetCardInfo("Light the Way", 24, Rarity.COMMON, mage.cards.l.LightTheWay.class)); - cards.add(new SetCardInfo("Light-Paws, Emperor's Voice", 25, Rarity.RARE, mage.cards.l.LightPawsEmperorsVoice.class)); + cards.add(new SetCardInfo("Light-Paws, Emperor's Voice", 25, Rarity.RARE, mage.cards.l.LightPawsEmperorsVoice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Light-Paws, Emperor's Voice", 367, Rarity.RARE, mage.cards.l.LightPawsEmperorsVoice.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Light-Paws, Emperor's Voice", 439, Rarity.RARE, mage.cards.l.LightPawsEmperorsVoice.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Likeness of the Seeker", 172, Rarity.UNCOMMON, mage.cards.l.LikenessOfTheSeeker.class)); - cards.add(new SetCardInfo("Lion Sash", 26, Rarity.RARE, mage.cards.l.LionSash.class)); - cards.add(new SetCardInfo("Living Breakthrough", 57, Rarity.RARE, mage.cards.l.LivingBreakthrough.class)); - cards.add(new SetCardInfo("Lizard Blades", 153, Rarity.RARE, mage.cards.l.LizardBlades.class)); + cards.add(new SetCardInfo("Lion Sash", 26, Rarity.RARE, mage.cards.l.LionSash.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lion Sash", 368, Rarity.RARE, mage.cards.l.LionSash.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lion Sash", 440, Rarity.RARE, mage.cards.l.LionSash.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Living Breakthrough", 355, Rarity.RARE, mage.cards.l.LivingBreakthrough.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Living Breakthrough", 443, Rarity.RARE, mage.cards.l.LivingBreakthrough.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Living Breakthrough", 57, Rarity.RARE, mage.cards.l.LivingBreakthrough.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lizard Blades", 153, Rarity.RARE, mage.cards.l.LizardBlades.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lizard Blades", 385, Rarity.RARE, mage.cards.l.LizardBlades.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Lizard Blades", 468, Rarity.RARE, mage.cards.l.LizardBlades.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Lucky Offering", 27, Rarity.COMMON, mage.cards.l.LuckyOffering.class)); cards.add(new SetCardInfo("Malicious Malfunction", 110, Rarity.UNCOMMON, mage.cards.m.MaliciousMalfunction.class)); - cards.add(new SetCardInfo("March of Burgeoning Life", 201, Rarity.RARE, mage.cards.m.MarchOfBurgeoningLife.class)); - cards.add(new SetCardInfo("March of Otherworldly Light", 28, Rarity.RARE, mage.cards.m.MarchOfOtherworldlyLight.class)); - cards.add(new SetCardInfo("March of Reckless Joy", 154, Rarity.RARE, mage.cards.m.MarchOfRecklessJoy.class)); - cards.add(new SetCardInfo("March of Swirling Mist", 61, Rarity.RARE, mage.cards.m.MarchOfSwirlingMist.class)); - cards.add(new SetCardInfo("March of Wretched Sorrow", 111, Rarity.RARE, mage.cards.m.MarchOfWretchedSorrow.class)); + cards.add(new SetCardInfo("March of Burgeoning Life", 201, Rarity.RARE, mage.cards.m.MarchOfBurgeoningLife.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Burgeoning Life", 393, Rarity.RARE, mage.cards.m.MarchOfBurgeoningLife.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Burgeoning Life", 479, Rarity.RARE, mage.cards.m.MarchOfBurgeoningLife.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Otherworldly Light", 28, Rarity.RARE, mage.cards.m.MarchOfOtherworldlyLight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Otherworldly Light", 369, Rarity.RARE, mage.cards.m.MarchOfOtherworldlyLight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Otherworldly Light", 441, Rarity.RARE, mage.cards.m.MarchOfOtherworldlyLight.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Reckless Joy", 154, Rarity.RARE, mage.cards.m.MarchOfRecklessJoy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Reckless Joy", 386, Rarity.RARE, mage.cards.m.MarchOfRecklessJoy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Reckless Joy", 469, Rarity.RARE, mage.cards.m.MarchOfRecklessJoy.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Swirling Mist", 372, Rarity.RARE, mage.cards.m.MarchOfSwirlingMist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Swirling Mist", 447, Rarity.RARE, mage.cards.m.MarchOfSwirlingMist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Swirling Mist", 61, Rarity.RARE, mage.cards.m.MarchOfSwirlingMist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Wretched Sorrow", 111, Rarity.RARE, mage.cards.m.MarchOfWretchedSorrow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Wretched Sorrow", 380, Rarity.RARE, mage.cards.m.MarchOfWretchedSorrow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("March of Wretched Sorrow", 457, Rarity.RARE, mage.cards.m.MarchOfWretchedSorrow.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Master's Rebuke", 202, Rarity.COMMON, mage.cards.m.MastersRebuke.class)); cards.add(new SetCardInfo("Mech Hangar", 270, Rarity.UNCOMMON, mage.cards.m.MechHangar.class)); - cards.add(new SetCardInfo("Mechtitan Core", 249, Rarity.RARE, mage.cards.m.MechtitanCore.class)); + cards.add(new SetCardInfo("Mechtitan Core", 249, Rarity.RARE, mage.cards.m.MechtitanCore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mechtitan Core", 402, Rarity.RARE, mage.cards.m.MechtitanCore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mechtitan Core", 497, Rarity.RARE, mage.cards.m.MechtitanCore.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Memory of Toshiro", 108, Rarity.UNCOMMON, mage.cards.m.MemoryOfToshiro.class)); cards.add(new SetCardInfo("Michiko's Reign of Truth", 29, Rarity.UNCOMMON, mage.cards.m.MichikosReignOfTruth.class)); - cards.add(new SetCardInfo("Mindlink Mech", 62, Rarity.RARE, mage.cards.m.MindlinkMech.class)); - cards.add(new SetCardInfo("Mirror Box", 250, Rarity.RARE, mage.cards.m.MirrorBox.class)); + cards.add(new SetCardInfo("Mindlink Mech", 373, Rarity.RARE, mage.cards.m.MindlinkMech.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mindlink Mech", 448, Rarity.RARE, mage.cards.m.MindlinkMech.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mindlink Mech", 62, Rarity.RARE, mage.cards.m.MindlinkMech.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirror Box", 250, Rarity.RARE, mage.cards.m.MirrorBox.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirror Box", 403, Rarity.RARE, mage.cards.m.MirrorBox.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mirror Box", 498, Rarity.RARE, mage.cards.m.MirrorBox.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mirrorshell Crab", 63, Rarity.COMMON, mage.cards.m.MirrorshellCrab.class)); cards.add(new SetCardInfo("Mnemonic Sphere", 64, Rarity.COMMON, mage.cards.m.MnemonicSphere.class)); cards.add(new SetCardInfo("Mobilizer Mech", 65, Rarity.UNCOMMON, mage.cards.m.MobilizerMech.class)); - cards.add(new SetCardInfo("Moon-Circuit Hacker", 67, Rarity.COMMON, mage.cards.m.MoonCircuitHacker.class)); + cards.add(new SetCardInfo("Moon-Circuit Hacker", 334, Rarity.COMMON, mage.cards.m.MoonCircuitHacker.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Moon-Circuit Hacker", 67, Rarity.COMMON, mage.cards.m.MoonCircuitHacker.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Moonfolk Puzzlemaker", 68, Rarity.COMMON, mage.cards.m.MoonfolkPuzzlemaker.class)); cards.add(new SetCardInfo("Moonsnare Prototype", 69, Rarity.COMMON, mage.cards.m.MoonsnarePrototype.class)); - cards.add(new SetCardInfo("Moonsnare Specialist", 70, Rarity.COMMON, mage.cards.m.MoonsnareSpecialist.class)); + cards.add(new SetCardInfo("Moonsnare Specialist", 335, Rarity.COMMON, mage.cards.m.MoonsnareSpecialist.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Moonsnare Specialist", 70, Rarity.COMMON, mage.cards.m.MoonsnareSpecialist.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Mothrider Patrol", 30, Rarity.COMMON, mage.cards.m.MothriderPatrol.class)); cards.add(new SetCardInfo("Mountain", 289, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); - cards.add(new SetCardInfo("Mukotai Ambusher", 112, Rarity.COMMON, mage.cards.m.MukotaiAmbusher.class)); - cards.add(new SetCardInfo("Mukotai Soulripper", 113, Rarity.RARE, mage.cards.m.MukotaiSoulripper.class)); + cards.add(new SetCardInfo("Mountain", 290, Rarity.LAND, mage.cards.basiclands.Mountain.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 299, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mountain", 300, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Mukotai Ambusher", 112, Rarity.COMMON, mage.cards.m.MukotaiAmbusher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mukotai Ambusher", 342, Rarity.COMMON, mage.cards.m.MukotaiAmbusher.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mukotai Soulripper", 113, Rarity.RARE, mage.cards.m.MukotaiSoulripper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mukotai Soulripper", 381, Rarity.RARE, mage.cards.m.MukotaiSoulripper.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Mukotai Soulripper", 458, Rarity.RARE, mage.cards.m.MukotaiSoulripper.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nameless Conqueror", 162, Rarity.COMMON, mage.cards.n.NamelessConqueror.class)); cards.add(new SetCardInfo("Naomi, Pillar of Order", 229, Rarity.UNCOMMON, mage.cards.n.NaomiPillarOfOrder.class)); - cards.add(new SetCardInfo("Nashi, Moon Sage's Scion", 114, Rarity.MYTHIC, mage.cards.n.NashiMoonSagesScion.class)); + cards.add(new SetCardInfo("Nashi, Moon Sage's Scion", 114, Rarity.MYTHIC, mage.cards.n.NashiMoonSagesScion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nashi, Moon Sage's Scion", 343, Rarity.MYTHIC, mage.cards.n.NashiMoonSagesScion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nashi, Moon Sage's Scion", 421, Rarity.MYTHIC, mage.cards.n.NashiMoonSagesScion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nashi, Moon Sage's Scion", 459, Rarity.MYTHIC, mage.cards.n.NashiMoonSagesScion.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Network Disruptor", 71, Rarity.COMMON, mage.cards.n.NetworkDisruptor.class)); cards.add(new SetCardInfo("Network Terminal", 251, Rarity.COMMON, mage.cards.n.NetworkTerminal.class)); - cards.add(new SetCardInfo("Nezumi Bladeblesser", 115, Rarity.COMMON, mage.cards.n.NezumiBladeblesser.class)); - cards.add(new SetCardInfo("Nezumi Prowler", 116, Rarity.UNCOMMON, mage.cards.n.NezumiProwler.class)); + cards.add(new SetCardInfo("Nezumi Bladeblesser", 115, Rarity.COMMON, mage.cards.n.NezumiBladeblesser.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nezumi Bladeblesser", 318, Rarity.COMMON, mage.cards.n.NezumiBladeblesser.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nezumi Prowler", 116, Rarity.UNCOMMON, mage.cards.n.NezumiProwler.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Nezumi Prowler", 344, Rarity.UNCOMMON, mage.cards.n.NezumiProwler.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Nezumi Road Captain", 117, Rarity.COMMON, mage.cards.n.NezumiRoadCaptain.class)); cards.add(new SetCardInfo("Ninja's Kunai", 252, Rarity.COMMON, mage.cards.n.NinjasKunai.class)); - cards.add(new SetCardInfo("Norika Yamazaki, the Poet", 31, Rarity.UNCOMMON, mage.cards.n.NorikaYamazakiThePoet.class)); - cards.add(new SetCardInfo("O-Kagachi Made Manifest", 227, Rarity.MYTHIC, mage.cards.o.OKagachiMadeManifest.class)); - cards.add(new SetCardInfo("Ogre-Head Helm", 155, Rarity.RARE, mage.cards.o.OgreHeadHelm.class)); + cards.add(new SetCardInfo("Norika Yamazaki, the Poet", 31, Rarity.UNCOMMON, mage.cards.n.NorikaYamazakiThePoet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Norika Yamazaki, the Poet", 311, Rarity.UNCOMMON, mage.cards.n.NorikaYamazakiThePoet.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("O-Kagachi Made Manifest", 227, Rarity.MYTHIC, mage.cards.o.OKagachiMadeManifest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("O-Kagachi Made Manifest", 362, Rarity.MYTHIC, mage.cards.o.OKagachiMadeManifest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("O-Kagachi Made Manifest", 489, Rarity.MYTHIC, mage.cards.o.OKagachiMadeManifest.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ogre-Head Helm", 155, Rarity.RARE, mage.cards.o.OgreHeadHelm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ogre-Head Helm", 387, Rarity.RARE, mage.cards.o.OgreHeadHelm.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Ogre-Head Helm", 470, Rarity.RARE, mage.cards.o.OgreHeadHelm.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Okiba Reckoner Raid", 117, Rarity.COMMON, mage.cards.o.OkibaReckonerRaid.class)); cards.add(new SetCardInfo("Okiba Salvage", 118, Rarity.UNCOMMON, mage.cards.o.OkibaSalvage.class)); cards.add(new SetCardInfo("Oni-Cult Anvil", 230, Rarity.UNCOMMON, mage.cards.o.OniCultAnvil.class)); cards.add(new SetCardInfo("Orochi Merge-Keeper", 203, Rarity.UNCOMMON, mage.cards.o.OrochiMergeKeeper.class)); - cards.add(new SetCardInfo("Otawara, Soaring City", 271, Rarity.RARE, mage.cards.o.OtawaraSoaringCity.class)); + cards.add(new SetCardInfo("Otawara, Soaring City", 271, Rarity.RARE, mage.cards.o.OtawaraSoaringCity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Otawara, Soaring City", 414, Rarity.RARE, mage.cards.o.OtawaraSoaringCity.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Otawara, Soaring City", 503, Rarity.RARE, mage.cards.o.OtawaraSoaringCity.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Papercraft Decoy", 253, Rarity.COMMON, mage.cards.p.PapercraftDecoy.class)); cards.add(new SetCardInfo("Patchwork Automaton", 254, Rarity.UNCOMMON, mage.cards.p.PatchworkAutomaton.class)); - cards.add(new SetCardInfo("Peerless Samurai", 156, Rarity.COMMON, mage.cards.p.PeerlessSamurai.class)); + cards.add(new SetCardInfo("Peerless Samurai", 156, Rarity.COMMON, mage.cards.p.PeerlessSamurai.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Peerless Samurai", 322, Rarity.COMMON, mage.cards.p.PeerlessSamurai.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Plains", 283, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 284, Rarity.LAND, mage.cards.basiclands.Plains.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Plains", 293, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Plains", 294, Rarity.LAND, mage.cards.basiclands.Plains.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Planar Incision", 72, Rarity.COMMON, mage.cards.p.PlanarIncision.class)); cards.add(new SetCardInfo("Portrait of Michiko", 29, Rarity.UNCOMMON, mage.cards.p.PortraitOfMichiko.class)); cards.add(new SetCardInfo("Prodigy's Prototype", 231, Rarity.UNCOMMON, mage.cards.p.ProdigysPrototype.class)); - cards.add(new SetCardInfo("Prosperous Thief", 73, Rarity.UNCOMMON, mage.cards.p.ProsperousThief.class)); + cards.add(new SetCardInfo("Prosperous Thief", 336, Rarity.UNCOMMON, mage.cards.p.ProsperousThief.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Prosperous Thief", 73, Rarity.UNCOMMON, mage.cards.p.ProsperousThief.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Rabbit Battery", 157, Rarity.UNCOMMON, mage.cards.r.RabbitBattery.class)); - cards.add(new SetCardInfo("Raiyuu, Storm's Edge", 232, Rarity.RARE, mage.cards.r.RaiyuuStormsEdge.class)); + cards.add(new SetCardInfo("Raiyuu, Storm's Edge", 232, Rarity.RARE, mage.cards.r.RaiyuuStormsEdge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Raiyuu, Storm's Edge", 329, Rarity.RARE, mage.cards.r.RaiyuuStormsEdge.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Raiyuu, Storm's Edge", 491, Rarity.RARE, mage.cards.r.RaiyuuStormsEdge.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Reality Heist", 75, Rarity.UNCOMMON, mage.cards.r.RealityHeist.class)); - cards.add(new SetCardInfo("Reckoner Bankbuster", 255, Rarity.RARE, mage.cards.r.ReckonerBankbuster.class)); + cards.add(new SetCardInfo("Reckoner Bankbuster", 255, Rarity.RARE, mage.cards.r.ReckonerBankbuster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reckoner Bankbuster", 404, Rarity.RARE, mage.cards.r.ReckonerBankbuster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reckoner Bankbuster", 499, Rarity.RARE, mage.cards.r.ReckonerBankbuster.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Reckoner Shakedown", 119, Rarity.COMMON, mage.cards.r.ReckonerShakedown.class)); cards.add(new SetCardInfo("Reckoner's Bargain", 120, Rarity.COMMON, mage.cards.r.ReckonersBargain.class)); - cards.add(new SetCardInfo("Reflection of Kiki-Jiki", 141, Rarity.RARE, mage.cards.r.ReflectionOfKikiJiki.class)); + cards.add(new SetCardInfo("Reflection of Kiki-Jiki", 141, Rarity.RARE, mage.cards.r.ReflectionOfKikiJiki.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reflection of Kiki-Jiki", 357, Rarity.RARE, mage.cards.r.ReflectionOfKikiJiki.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reflection of Kiki-Jiki", 465, Rarity.RARE, mage.cards.r.ReflectionOfKikiJiki.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Regent's Authority", 32, Rarity.COMMON, mage.cards.r.RegentsAuthority.class)); - cards.add(new SetCardInfo("Reinforced Ronin", 158, Rarity.UNCOMMON, mage.cards.r.ReinforcedRonin.class)); + cards.add(new SetCardInfo("Reinforced Ronin", 158, Rarity.UNCOMMON, mage.cards.r.ReinforcedRonin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Reinforced Ronin", 323, Rarity.UNCOMMON, mage.cards.r.ReinforcedRonin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Reito Sentinel", 256, Rarity.UNCOMMON, mage.cards.r.ReitoSentinel.class)); - cards.add(new SetCardInfo("Remnant of the Rising Star", 194, Rarity.MYTHIC, mage.cards.r.RemnantOfTheRisingStar.class)); + cards.add(new SetCardInfo("Remnant of the Rising Star", 194, Rarity.MYTHIC, mage.cards.r.RemnantOfTheRisingStar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Remnant of the Rising Star", 359, Rarity.MYTHIC, mage.cards.r.RemnantOfTheRisingStar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Remnant of the Rising Star", 475, Rarity.MYTHIC, mage.cards.r.RemnantOfTheRisingStar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Repel the Vile", 33, Rarity.COMMON, mage.cards.r.RepelTheVile.class)); cards.add(new SetCardInfo("Replication Specialist", 76, Rarity.UNCOMMON, mage.cards.r.ReplicationSpecialist.class)); cards.add(new SetCardInfo("Return to Action", 121, Rarity.COMMON, mage.cards.r.ReturnToAction.class)); - cards.add(new SetCardInfo("Risona, Asari Commander", 233, Rarity.RARE, mage.cards.r.RisonaAsariCommander.class)); + cards.add(new SetCardInfo("Risona, Asari Commander", 233, Rarity.RARE, mage.cards.r.RisonaAsariCommander.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Risona, Asari Commander", 330, Rarity.RARE, mage.cards.r.RisonaAsariCommander.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Risona, Asari Commander", 425, Rarity.RARE, mage.cards.r.RisonaAsariCommander.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Risona, Asari Commander", 492, Rarity.RARE, mage.cards.r.RisonaAsariCommander.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Roadside Reliquary", 272, Rarity.UNCOMMON, mage.cards.r.RoadsideReliquary.class)); cards.add(new SetCardInfo("Roaring Earth", 204, Rarity.UNCOMMON, mage.cards.r.RoaringEarth.class)); cards.add(new SetCardInfo("Rugged Highlands", 273, Rarity.COMMON, mage.cards.r.RuggedHighlands.class)); cards.add(new SetCardInfo("Runaway Trash-Bot", 257, Rarity.UNCOMMON, mage.cards.r.RunawayTrashBot.class)); cards.add(new SetCardInfo("Saiba Trespassers", 77, Rarity.COMMON, mage.cards.s.SaibaTrespassers.class)); - cards.add(new SetCardInfo("Satoru Umezawa", 234, Rarity.RARE, mage.cards.s.SatoruUmezawa.class)); - cards.add(new SetCardInfo("Satsuki, the Living Lore", 235, Rarity.RARE, mage.cards.s.SatsukiTheLivingLore.class)); + cards.add(new SetCardInfo("Satoru Umezawa", 234, Rarity.RARE, mage.cards.s.SatoruUmezawa.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Satoru Umezawa", 352, Rarity.RARE, mage.cards.s.SatoruUmezawa.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Satoru Umezawa", 426, Rarity.RARE, mage.cards.s.SatoruUmezawa.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Satoru Umezawa", 493, Rarity.RARE, mage.cards.s.SatoruUmezawa.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Satoru Umezawa", 507, Rarity.RARE, mage.cards.s.SatoruUmezawa.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Satsuki, the Living Lore", 235, Rarity.RARE, mage.cards.s.SatsukiTheLivingLore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Satsuki, the Living Lore", 399, Rarity.RARE, mage.cards.s.SatsukiTheLivingLore.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Satsuki, the Living Lore", 494, Rarity.RARE, mage.cards.s.SatsukiTheLivingLore.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Scoured Barrens", 274, Rarity.COMMON, mage.cards.s.ScouredBarrens.class)); - cards.add(new SetCardInfo("Scrap Welder", 159, Rarity.RARE, mage.cards.s.ScrapWelder.class)); + cards.add(new SetCardInfo("Scrap Welder", 159, Rarity.RARE, mage.cards.s.ScrapWelder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scrap Welder", 388, Rarity.RARE, mage.cards.s.ScrapWelder.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Scrap Welder", 471, Rarity.RARE, mage.cards.s.ScrapWelder.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Scrapyard Steelbreaker", 160, Rarity.COMMON, mage.cards.s.ScrapyardSteelbreaker.class)); cards.add(new SetCardInfo("Searchlight Companion", 258, Rarity.COMMON, mage.cards.s.SearchlightCompanion.class)); cards.add(new SetCardInfo("Season of Renewal", 205, Rarity.COMMON, mage.cards.s.SeasonOfRenewal.class)); - cards.add(new SetCardInfo("Secluded Courtyard", 275, Rarity.UNCOMMON, mage.cards.s.SecludedCourtyard.class)); + cards.add(new SetCardInfo("Secluded Courtyard", 275, Rarity.UNCOMMON, mage.cards.s.SecludedCourtyard.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Secluded Courtyard", 512, Rarity.UNCOMMON, mage.cards.s.SecludedCourtyard.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Seismic Wave", 161, Rarity.UNCOMMON, mage.cards.s.SeismicWave.class)); - cards.add(new SetCardInfo("Selfless Samurai", 35, Rarity.UNCOMMON, mage.cards.s.SelflessSamurai.class)); + cards.add(new SetCardInfo("Selfless Samurai", 312, Rarity.UNCOMMON, mage.cards.s.SelflessSamurai.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Selfless Samurai", 35, Rarity.UNCOMMON, mage.cards.s.SelflessSamurai.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Seshiro's Living Legacy", 210, Rarity.COMMON, mage.cards.s.SeshirosLivingLegacy.class)); - cards.add(new SetCardInfo("Seven-Tail Mentor", 36, Rarity.COMMON, mage.cards.s.SevenTailMentor.class)); - cards.add(new SetCardInfo("Shigeki, Jukai Visionary", 206, Rarity.RARE, mage.cards.s.ShigekiJukaiVisionary.class)); + cards.add(new SetCardInfo("Seven-Tail Mentor", 313, Rarity.COMMON, mage.cards.s.SevenTailMentor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Seven-Tail Mentor", 36, Rarity.COMMON, mage.cards.s.SevenTailMentor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shigeki, Jukai Visionary", 206, Rarity.RARE, mage.cards.s.ShigekiJukaiVisionary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shigeki, Jukai Visionary", 394, Rarity.RARE, mage.cards.s.ShigekiJukaiVisionary.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Shigeki, Jukai Visionary", 480, Rarity.RARE, mage.cards.s.ShigekiJukaiVisionary.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Short Circuit", 78, Rarity.COMMON, mage.cards.s.ShortCircuit.class)); cards.add(new SetCardInfo("Shrine Steward", 259, Rarity.COMMON, mage.cards.s.ShrineSteward.class)); - cards.add(new SetCardInfo("Silver-Fur Master", 236, Rarity.UNCOMMON, mage.cards.s.SilverFurMaster.class)); + cards.add(new SetCardInfo("Silver-Fur Master", 236, Rarity.UNCOMMON, mage.cards.s.SilverFurMaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Silver-Fur Master", 353, Rarity.UNCOMMON, mage.cards.s.SilverFurMaster.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Silver-Fur Master", 511, Rarity.UNCOMMON, mage.cards.s.SilverFurMaster.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Simian Sling", 163, Rarity.COMMON, mage.cards.s.SimianSling.class)); - cards.add(new SetCardInfo("Sky-Blessed Samurai", 37, Rarity.UNCOMMON, mage.cards.s.SkyBlessedSamurai.class)); + cards.add(new SetCardInfo("Sky-Blessed Samurai", 314, Rarity.UNCOMMON, mage.cards.s.SkyBlessedSamurai.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sky-Blessed Samurai", 37, Rarity.UNCOMMON, mage.cards.s.SkyBlessedSamurai.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Skyswimmer Koi", 79, Rarity.COMMON, mage.cards.s.SkyswimmerKoi.class)); cards.add(new SetCardInfo("Sokenzan Smelter", 164, Rarity.UNCOMMON, mage.cards.s.SokenzanSmelter.class)); - cards.add(new SetCardInfo("Sokenzan, Crucible of Defiance", 276, Rarity.RARE, mage.cards.s.SokenzanCrucibleOfDefiance.class)); - cards.add(new SetCardInfo("Soul Transfer", 122, Rarity.RARE, mage.cards.s.SoulTransfer.class)); + cards.add(new SetCardInfo("Sokenzan, Crucible of Defiance", 276, Rarity.RARE, mage.cards.s.SokenzanCrucibleOfDefiance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sokenzan, Crucible of Defiance", 415, Rarity.RARE, mage.cards.s.SokenzanCrucibleOfDefiance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sokenzan, Crucible of Defiance", 504, Rarity.RARE, mage.cards.s.SokenzanCrucibleOfDefiance.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soul Transfer", 122, Rarity.RARE, mage.cards.s.SoulTransfer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soul Transfer", 382, Rarity.RARE, mage.cards.s.SoulTransfer.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Soul Transfer", 460, Rarity.RARE, mage.cards.s.SoulTransfer.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Spell Pierce", 80, Rarity.COMMON, mage.cards.s.SpellPierce.class)); cards.add(new SetCardInfo("Spinning Wheel Kick", 207, Rarity.UNCOMMON, mage.cards.s.SpinningWheelKick.class)); - cards.add(new SetCardInfo("Spirit-Sister's Call", 237, Rarity.MYTHIC, mage.cards.s.SpiritSistersCall.class)); - cards.add(new SetCardInfo("Spirited Companion", 38, Rarity.COMMON, mage.cards.s.SpiritedCompanion.class)); - cards.add(new SetCardInfo("Spring-Leaf Avenger", 208, Rarity.RARE, mage.cards.s.SpringLeafAvenger.class)); + cards.add(new SetCardInfo("Spirit-Sister's Call", 237, Rarity.MYTHIC, mage.cards.s.SpiritSistersCall.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spirit-Sister's Call", 400, Rarity.MYTHIC, mage.cards.s.SpiritSistersCall.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spirit-Sister's Call", 495, Rarity.MYTHIC, mage.cards.s.SpiritSistersCall.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spirited Companion", 38, Rarity.COMMON, mage.cards.s.SpiritedCompanion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spirited Companion", 508, Rarity.COMMON, mage.cards.s.SpiritedCompanion.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spring-Leaf Avenger", 208, Rarity.RARE, mage.cards.s.SpringLeafAvenger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spring-Leaf Avenger", 349, Rarity.RARE, mage.cards.s.SpringLeafAvenger.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Spring-Leaf Avenger", 481, Rarity.RARE, mage.cards.s.SpringLeafAvenger.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Storyweave", 209, Rarity.UNCOMMON, mage.cards.s.Storyweave.class)); cards.add(new SetCardInfo("Suit Up", 81, Rarity.COMMON, mage.cards.s.SuitUp.class)); - cards.add(new SetCardInfo("Sunblade Samurai", 39, Rarity.COMMON, mage.cards.s.SunbladeSamurai.class)); - cards.add(new SetCardInfo("Surgehacker Mech", 260, Rarity.RARE, mage.cards.s.SurgehackerMech.class)); + cards.add(new SetCardInfo("Sunblade Samurai", 315, Rarity.COMMON, mage.cards.s.SunbladeSamurai.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Sunblade Samurai", 39, Rarity.COMMON, mage.cards.s.SunbladeSamurai.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Surgehacker Mech", 260, Rarity.RARE, mage.cards.s.SurgehackerMech.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Surgehacker Mech", 405, Rarity.RARE, mage.cards.s.SurgehackerMech.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Surgehacker Mech", 500, Rarity.RARE, mage.cards.s.SurgehackerMech.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Swamp", 287, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 288, Rarity.LAND, mage.cards.basiclands.Swamp.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 297, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); + cards.add(new SetCardInfo("Swamp", 298, Rarity.LAND, mage.cards.basiclands.Swamp.class, FULL_ART_BFZ_VARIOUS)); cards.add(new SetCardInfo("Swiftwater Cliffs", 277, Rarity.COMMON, mage.cards.s.SwiftwaterCliffs.class)); - cards.add(new SetCardInfo("Takenuma, Abandoned Mire", 278, Rarity.RARE, mage.cards.t.TakenumaAbandonedMire.class)); + cards.add(new SetCardInfo("Takenuma, Abandoned Mire", 278, Rarity.RARE, mage.cards.t.TakenumaAbandonedMire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Takenuma, Abandoned Mire", 416, Rarity.RARE, mage.cards.t.TakenumaAbandonedMire.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Takenuma, Abandoned Mire", 505, Rarity.RARE, mage.cards.t.TakenumaAbandonedMire.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tales of Master Seshiro", 210, Rarity.COMMON, mage.cards.t.TalesOfMasterSeshiro.class)); - cards.add(new SetCardInfo("Tameshi, Reality Architect", 82, Rarity.RARE, mage.cards.t.TameshiRealityArchitect.class)); + cards.add(new SetCardInfo("Tameshi, Reality Architect", 375, Rarity.RARE, mage.cards.t.TameshiRealityArchitect.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tameshi, Reality Architect", 450, Rarity.RARE, mage.cards.t.TameshiRealityArchitect.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tameshi, Reality Architect", 82, Rarity.RARE, mage.cards.t.TameshiRealityArchitect.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tamiyo's Compleation", 83, Rarity.COMMON, mage.cards.t.TamiyosCompleation.class)); cards.add(new SetCardInfo("Tamiyo's Safekeeping", 211, Rarity.COMMON, mage.cards.t.TamiyosSafekeeping.class)); - cards.add(new SetCardInfo("Tamiyo, Compleated Sage", 238, Rarity.MYTHIC, mage.cards.t.TamiyoCompleatedSage.class)); - cards.add(new SetCardInfo("Tatsunari, Toad Rider", 123, Rarity.RARE, mage.cards.t.TatsunariToadRider.class)); - cards.add(new SetCardInfo("Teachings of the Kirin", 212, Rarity.RARE, mage.cards.t.TeachingsOfTheKirin.class)); + cards.add(new SetCardInfo("Tamiyo, Compleated Sage", 238, Rarity.MYTHIC, mage.cards.t.TamiyoCompleatedSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tamiyo, Compleated Sage", 306, Rarity.MYTHIC, mage.cards.t.TamiyoCompleatedSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tamiyo, Compleated Sage", 308, Rarity.MYTHIC, mage.cards.t.TamiyoCompleatedSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tamiyo, Compleated Sage", 428, Rarity.MYTHIC, mage.cards.t.TamiyoCompleatedSage.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tatsunari, Toad Rider", 123, Rarity.RARE, mage.cards.t.TatsunariToadRider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tatsunari, Toad Rider", 345, Rarity.RARE, mage.cards.t.TatsunariToadRider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tatsunari, Toad Rider", 461, Rarity.RARE, mage.cards.t.TatsunariToadRider.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teachings of the Kirin", 212, Rarity.RARE, mage.cards.t.TeachingsOfTheKirin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teachings of the Kirin", 360, Rarity.RARE, mage.cards.t.TeachingsOfTheKirin.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Teachings of the Kirin", 482, Rarity.RARE, mage.cards.t.TeachingsOfTheKirin.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Tempered in Solitude", 165, Rarity.UNCOMMON, mage.cards.t.TemperedInSolitude.class)); - cards.add(new SetCardInfo("Tezzeret, Betrayer of Flesh", 84, Rarity.MYTHIC, mage.cards.t.TezzeretBetrayerOfFlesh.class)); + cards.add(new SetCardInfo("Tezzeret, Betrayer of Flesh", 304, Rarity.MYTHIC, mage.cards.t.TezzeretBetrayerOfFlesh.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tezzeret, Betrayer of Flesh", 376, Rarity.MYTHIC, mage.cards.t.TezzeretBetrayerOfFlesh.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tezzeret, Betrayer of Flesh", 419, Rarity.MYTHIC, mage.cards.t.TezzeretBetrayerOfFlesh.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tezzeret, Betrayer of Flesh", 84, Rarity.MYTHIC, mage.cards.t.TezzeretBetrayerOfFlesh.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Dragon-Kami Reborn", 181, Rarity.RARE, mage.cards.t.TheDragonKamiReborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Dragon-Kami Reborn", 358, Rarity.RARE, mage.cards.t.TheDragonKamiReborn.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Dragon-Kami Reborn", 473, Rarity.RARE, mage.cards.t.TheDragonKamiReborn.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Fall of Lord Konda", 12, Rarity.UNCOMMON, mage.cards.t.TheFallOfLordKonda.class)); - cards.add(new SetCardInfo("The Kami War", 227, Rarity.MYTHIC, mage.cards.t.TheKamiWar.class)); + cards.add(new SetCardInfo("The Kami War", 227, Rarity.MYTHIC, mage.cards.t.TheKamiWar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Kami War", 362, Rarity.MYTHIC, mage.cards.t.TheKamiWar.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Kami War", 489, Rarity.MYTHIC, mage.cards.t.TheKamiWar.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Long Reach of Night", 109, Rarity.UNCOMMON, mage.cards.t.TheLongReachOfNight.class)); cards.add(new SetCardInfo("The Modern Age", 66, Rarity.COMMON, mage.cards.t.TheModernAge.class)); - cards.add(new SetCardInfo("The Reality Chip", 74, Rarity.RARE, mage.cards.t.TheRealityChip.class)); - cards.add(new SetCardInfo("The Restoration of Eiganjo", 34, Rarity.RARE, mage.cards.t.TheRestorationOfEiganjo.class)); + cards.add(new SetCardInfo("The Reality Chip", 374, Rarity.RARE, mage.cards.t.TheRealityChip.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Reality Chip", 449, Rarity.RARE, mage.cards.t.TheRealityChip.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Reality Chip", 74, Rarity.RARE, mage.cards.t.TheRealityChip.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Restoration of Eiganjo", 34, Rarity.RARE, mage.cards.t.TheRestorationOfEiganjo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Restoration of Eiganjo", 354, Rarity.RARE, mage.cards.t.TheRestorationOfEiganjo.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Restoration of Eiganjo", 442, Rarity.RARE, mage.cards.t.TheRestorationOfEiganjo.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("The Shattered States Era", 162, Rarity.COMMON, mage.cards.t.TheShatteredStatesEra.class)); - cards.add(new SetCardInfo("The Wandering Emperor", 42, Rarity.MYTHIC, mage.cards.t.TheWanderingEmperor.class)); + cards.add(new SetCardInfo("The Wandering Emperor", 303, Rarity.MYTHIC, mage.cards.t.TheWanderingEmperor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wandering Emperor", 316, Rarity.MYTHIC, mage.cards.t.TheWanderingEmperor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wandering Emperor", 418, Rarity.MYTHIC, mage.cards.t.TheWanderingEmperor.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("The Wandering Emperor", 42, Rarity.MYTHIC, mage.cards.t.TheWanderingEmperor.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Thirst for Knowledge", 85, Rarity.UNCOMMON, mage.cards.t.ThirstForKnowledge.class)); cards.add(new SetCardInfo("Thornwood Falls", 279, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class)); - cards.add(new SetCardInfo("Thousand-Faced Shadow", 86, Rarity.RARE, mage.cards.t.ThousandFacedShadow.class)); - cards.add(new SetCardInfo("Thundering Raiju", 166, Rarity.RARE, mage.cards.t.ThunderingRaiju.class)); + cards.add(new SetCardInfo("Thousand-Faced Shadow", 337, Rarity.RARE, mage.cards.t.ThousandFacedShadow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thousand-Faced Shadow", 451, Rarity.RARE, mage.cards.t.ThousandFacedShadow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thousand-Faced Shadow", 86, Rarity.RARE, mage.cards.t.ThousandFacedShadow.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thundering Raiju", 166, Rarity.RARE, mage.cards.t.ThunderingRaiju.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thundering Raiju", 389, Rarity.RARE, mage.cards.t.ThunderingRaiju.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Thundering Raiju", 472, Rarity.RARE, mage.cards.t.ThunderingRaiju.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Thundersteel Colossus", 261, Rarity.COMMON, mage.cards.t.ThundersteelColossus.class)); cards.add(new SetCardInfo("Touch the Spirit Realm", 40, Rarity.UNCOMMON, mage.cards.t.TouchTheSpiritRealm.class)); cards.add(new SetCardInfo("Towashi Guide-Bot", 262, Rarity.UNCOMMON, mage.cards.t.TowashiGuideBot.class)); cards.add(new SetCardInfo("Towashi Songshaper", 167, Rarity.COMMON, mage.cards.t.TowashiSongshaper.class)); cards.add(new SetCardInfo("Tranquil Cove", 280, Rarity.COMMON, mage.cards.t.TranquilCove.class)); - cards.add(new SetCardInfo("Tribute to Horobi", 124, Rarity.RARE, mage.cards.t.TributeToHorobi.class)); + cards.add(new SetCardInfo("Tribute to Horobi", 124, Rarity.RARE, mage.cards.t.TributeToHorobi.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tribute to Horobi", 356, Rarity.RARE, mage.cards.t.TributeToHorobi.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Tribute to Horobi", 462, Rarity.RARE, mage.cards.t.TributeToHorobi.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Twinshot Sniper", 168, Rarity.UNCOMMON, mage.cards.t.TwinshotSniper.class)); cards.add(new SetCardInfo("Twisted Embrace", 125, Rarity.COMMON, mage.cards.t.TwistedEmbrace.class)); cards.add(new SetCardInfo("Uncharted Haven", 281, Rarity.COMMON, mage.cards.u.UnchartedHaven.class)); cards.add(new SetCardInfo("Undercity Scrounger", 126, Rarity.COMMON, mage.cards.u.UndercityScrounger.class)); cards.add(new SetCardInfo("Unforgiving One", 127, Rarity.UNCOMMON, mage.cards.u.UnforgivingOne.class)); cards.add(new SetCardInfo("Unstoppable Ogre", 169, Rarity.COMMON, mage.cards.u.UnstoppableOgre.class)); - cards.add(new SetCardInfo("Upriser Renegade", 170, Rarity.UNCOMMON, mage.cards.u.UpriserRenegade.class)); + cards.add(new SetCardInfo("Upriser Renegade", 170, Rarity.UNCOMMON, mage.cards.u.UpriserRenegade.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Upriser Renegade", 324, Rarity.UNCOMMON, mage.cards.u.UpriserRenegade.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Vector Glider", 66, Rarity.COMMON, mage.cards.v.VectorGlider.class)); - cards.add(new SetCardInfo("Vessel of the All-Consuming", 221, Rarity.MYTHIC, mage.cards.v.VesselOfTheAllConsuming.class)); + cards.add(new SetCardInfo("Vessel of the All-Consuming", 221, Rarity.MYTHIC, mage.cards.v.VesselOfTheAllConsuming.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vessel of the All-Consuming", 361, Rarity.MYTHIC, mage.cards.v.VesselOfTheAllConsuming.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Vessel of the All-Consuming", 486, Rarity.MYTHIC, mage.cards.v.VesselOfTheAllConsuming.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Virus Beetle", 128, Rarity.COMMON, mage.cards.v.VirusBeetle.class)); cards.add(new SetCardInfo("Vision of the Unspeakable", 48, Rarity.UNCOMMON, mage.cards.v.VisionOfTheUnspeakable.class)); cards.add(new SetCardInfo("Voltage Surge", 171, Rarity.COMMON, mage.cards.v.VoltageSurge.class)); cards.add(new SetCardInfo("Walking Skyscraper", 263, Rarity.UNCOMMON, mage.cards.w.WalkingSkyscraper.class)); cards.add(new SetCardInfo("Wanderer's Intervention", 41, Rarity.COMMON, mage.cards.w.WanderersIntervention.class)); - cards.add(new SetCardInfo("Weaver of Harmony", 213, Rarity.RARE, mage.cards.w.WeaverOfHarmony.class)); + cards.add(new SetCardInfo("Weaver of Harmony", 213, Rarity.RARE, mage.cards.w.WeaverOfHarmony.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Weaver of Harmony", 395, Rarity.RARE, mage.cards.w.WeaverOfHarmony.class, NON_FULL_USE_VARIOUS)); + cards.add(new SetCardInfo("Weaver of Harmony", 483, Rarity.RARE, mage.cards.w.WeaverOfHarmony.class, NON_FULL_USE_VARIOUS)); cards.add(new SetCardInfo("Webspinner Cuff", 214, Rarity.UNCOMMON, mage.cards.w.WebspinnerCuff.class)); cards.add(new SetCardInfo("When We Were Young", 43, Rarity.UNCOMMON, mage.cards.w.WhenWeWereYoung.class)); cards.add(new SetCardInfo("Wind-Scarred Crag", 282, Rarity.COMMON, mage.cards.w.WindScarredCrag.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BuybackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BuybackTest.java index fc6f4b7629c..293ca054c73 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BuybackTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/BuybackTest.java @@ -143,7 +143,7 @@ public class BuybackTest extends CardTestPlayerBase { // bolt 2 - cast (R) and copy as free cast (R), return reiterate with buyback (RRR) castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerA); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, {T}:"); - setChoice(playerA, "Reiterate"); // free cast + setChoice(playerA, true); // cast for free setChoice(playerA, true); // use buyback addTarget(playerA, "Lightning Bolt"); // copy target setChoice(playerA, false); // same bolt's target diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerTest.java index 2b4e91d129a..2630bf37fad 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerTest.java @@ -670,7 +670,6 @@ public class KickerTest extends CardTestPlayerBase { // attack and prepare free cast, use kicker attack(1, playerA, "Etali, Primal Storm", playerB); setChoice(playerA, true); // cast for free - setChoice(playerA, "Ardent Soldier"); // cast for free setChoice(playerA, true); // use kicker setStrictChooseMode(true); @@ -703,7 +702,6 @@ public class KickerTest extends CardTestPlayerBase { // attack and prepare free cast attack(1, playerA, "Etali, Primal Storm", playerB); setChoice(playerA, true); // cast for free - setChoice(playerA, "Thieving Skydiver"); // cast for free setChoice(playerA, true); // use kicker setChoiceAmount(playerA, 2); // X=2 for Kicker X addTarget(playerA, "Brain in a Jar"); // kicker's target (take control of artifact) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java index 16a66f2dad3..3f8d5fffc89 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/adventure/AdventureCardsTest.java @@ -766,4 +766,122 @@ public class AdventureCardsTest extends CardTestPlayerBase { execute(); assertAllCommandsUsed(); } + + @Test + public void test_Cascade_CuriousPair() { + // If a player cascades into Curious Pair with Bloodbraid Elf they can cast either spell + removeAllCardsFromLibrary(playerA); + skipInitShuffling(); + + // Cascade + addCard(Zone.HAND, playerA, "Bloodbraid Elf"); // {2}{R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // + addCard(Zone.LIBRARY, playerA, "Swamp", 2); + addCard(Zone.LIBRARY, playerA, "Curious Pair", 1); + addCard(Zone.LIBRARY, playerA, "Island", 2); + + // play elf with cascade + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodbraid Elf"); + setChoice(playerA, true); // use free cast + setChoice(playerA, "Cast Treats to Share"); // can cast either + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Curious Pair", 0); + assertPermanentCount(playerA, "Food", 1); + assertExileCount(playerA, "Curious Pair", 1); + } + + @Test + public void test_Cascade_FlaxenIntruder() { + // If a player cascades into Flaxen Intruder with Bloodbraid Elf they shouldn't be able to cast Welcome Home + removeAllCardsFromLibrary(playerA); + skipInitShuffling(); + + // Cascade + addCard(Zone.HAND, playerA, "Bloodbraid Elf"); // {2}{R}{G} + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + // + addCard(Zone.LIBRARY, playerA, "Swamp", 2); + addCard(Zone.LIBRARY, playerA, "Flaxen Intruder", 1); + addCard(Zone.LIBRARY, playerA, "Island", 2); + + // play elf with cascade + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodbraid Elf"); + setChoice(playerA, true); // use free cast + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Flaxen Intruder", 1); + assertPermanentCount(playerA, "Bear", 0); + } + + @Test + public void test_SramsExpertise_CuriousPair() { + addCard(Zone.HAND, playerA, "Sram's Expertise"); + addCard(Zone.HAND, playerA, "Curious Pair"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sram's Expertise"); + setChoice(playerA, true); // use free cast + setChoice(playerA, "Cast Treats to Share"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Curious Pair", 0); + assertPermanentCount(playerA, "Food", 1); + assertPermanentCount(playerA, "Servo", 3); + assertExileCount(playerA, "Curious Pair", 1); + } + + @Test + public void test_SramsExpertise_FlaxenIntruder() { + addCard(Zone.HAND, playerA, "Sram's Expertise"); + addCard(Zone.HAND, playerA, "Flaxen Intruder"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sram's Expertise"); + setChoice(playerA, true); // use free cast + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Flaxen Intruder", 1); + assertPermanentCount(playerA, "Bear", 0); + assertPermanentCount(playerA, "Servo", 3); + } + + @Test + public void test_SramsExpertise_LonesomeUnicorn() { + addCard(Zone.HAND, playerA, "Sram's Expertise"); + addCard(Zone.HAND, playerA, "Lonesome Unicorn"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sram's Expertise"); + setChoice(playerA, true); // use free cast + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Lonesome Unicorn", 0); + assertPermanentCount(playerA, "Knight", 1); + assertPermanentCount(playerA, "Servo", 3); + assertExileCount(playerA, "Lonesome Unicorn", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java index 5102c3debc7..9e869954a3e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/modaldoublefaces/ModalDoubleFacesCardsTest.java @@ -866,6 +866,44 @@ public class ModalDoubleFacesCardsTest extends CardTestPlayerBase { assertPermanentCount(playerA, "The Omenkeel", 1); } + @Test + public void test_SramsExpertise_ValkiGodOfLies() { + addCard(Zone.HAND, playerA, "Sram's Expertise"); + addCard(Zone.HAND, playerA, "Valki, God of Lies"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sram's Expertise"); + setChoice(playerA, true); // use free cast + setChoice(playerA, TestPlayer.CHOICE_SKIP); // no choices for valki's etb exile + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "Valki, God of Lies", 1); + assertPermanentCount(playerA, "Servo", 3); + } + + @Test + public void test_SramsExpertise_CosimaGodOfTheVoyage() { + addCard(Zone.HAND, playerA, "Sram's Expertise"); + addCard(Zone.HAND, playerA, "Cosima, God of the Voyage"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sram's Expertise"); + setChoice(playerA, true); // use free cast + setChoice(playerA, "Cast The Omenkeel"); // can cast any side here + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, "The Omenkeel", 1); + assertPermanentCount(playerA, "Servo", 3); + } + @Test public void test_Copy_AsSpell() { addCard(Zone.HAND, playerA, "Akoum Warrior", 1); // {5}{R} @@ -1000,4 +1038,4 @@ public class ModalDoubleFacesCardsTest extends CardTestPlayerBase { execute(); assertAllCommandsUsed(); } -} \ No newline at end of file +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java index 8afb4166c59..f3dfbbd34f7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/splitcards/CastSplitCardsFromOtherZonesTest.java @@ -34,7 +34,6 @@ public class CastSplitCardsFromOtherZonesTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mindclaw Shaman"); addTarget(playerA, playerB); - setChoice(playerA, "Wear // Tear"); // select card setChoice(playerA, true); // confirm to cast setChoice(playerA, "Cast Tear"); // select tear side addTarget(playerA, "Sanguine Bond"); // target for tear @@ -67,7 +66,6 @@ public class CastSplitCardsFromOtherZonesTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mindclaw Shaman"); addTarget(playerA, playerB); - setChoice(playerA, "Wear // Tear"); // select card setChoice(playerA, true); // confirm to cast setChoice(playerA, "Cast Wear"); // select wear side addTarget(playerA, "Icy Manipulator"); // target for wear @@ -100,7 +98,6 @@ public class CastSplitCardsFromOtherZonesTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mindclaw Shaman"); addTarget(playerA, playerB); - setChoice(playerA, "Wear // Tear"); // select card setChoice(playerA, true); // confirm to cast setChoice(playerA, "Cast fused Wear // Tear"); // select fused addTarget(playerA, "Icy Manipulator"); // target for wear @@ -137,7 +134,6 @@ public class CastSplitCardsFromOtherZonesTest extends CardTestPlayerBase { attack(2, playerB, "Etali, Primal Storm"); setChoice(playerB, true); // free cast - setChoice(playerB, "Fire // Ice"); // card to cast setChoice(playerB, "Cast Fire"); // ability to cast addTargetAmount(playerB, "Silvercoat Lion", 2); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 2a61c4fdda5..30e26d5467d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -4364,19 +4364,17 @@ public class TestPlayer implements Player { @Override public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) { assertAliasSupportInChoices(false); - MageObject object = game.getObject(card.getId()); // must be object to find real abilities (example: commander) - Map useable = PlayerImpl.getCastableSpellAbilities(game, this.getId(), object, game.getState().getZone(object.getId()), noMana); - String allInfo = useable.values().stream().map(Object::toString).collect(Collectors.joining("\n")); + Map useable = PlayerImpl.getCastableSpellAbilities(game, this.getId(), object, game.getState().getZone(object.getId()), noMana); if (useable.size() == 1) { - return (SpellAbility) useable.values().iterator().next(); + return useable.values().iterator().next(); } if (!choices.isEmpty()) { - for (ActivatedAbility ability : useable.values()) { + for (SpellAbility ability : useable.values()) { if (ability.toString().startsWith(choices.get(0))) { choices.remove(0); - return (SpellAbility) ability; + return ability; } } @@ -4384,6 +4382,7 @@ public class TestPlayer implements Player { //Assert.fail("Wrong choice"); } + String allInfo = useable.values().stream().map(Object::toString).collect(Collectors.joining("\n")); this.chooseStrictModeFailed("choice", game, getInfo(card) + " - can't select ability to cast.\n" + "Card's abilities:\n" + allInfo); return computerPlayer.chooseAbilityForCast(card, game, noMana); } diff --git a/Mage/src/main/java/mage/abilities/SpellAbility.java b/Mage/src/main/java/mage/abilities/SpellAbility.java index 53db5121fc1..96c18cc532b 100644 --- a/Mage/src/main/java/mage/abilities/SpellAbility.java +++ b/Mage/src/main/java/mage/abilities/SpellAbility.java @@ -7,6 +7,7 @@ import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.keyword.FlashAbility; +import mage.cards.AdventureCardSpell; import mage.cards.Card; import mage.cards.SplitCard; import mage.constants.*; @@ -69,7 +70,7 @@ public class SpellAbility extends ActivatedAbilityImpl { // forced to cast (can be part id or main id) Set idsToCheck = new HashSet<>(); idsToCheck.add(object.getId()); - if (object instanceof Card) { + if (object instanceof Card && !(object instanceof AdventureCardSpell)) { idsToCheck.add(((Card) object).getMainCard().getId()); } for (UUID idToCheck : idsToCheck) { diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/CastFromHandForFreeEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/CastFromHandForFreeEffect.java new file mode 100644 index 00000000000..17311bbce18 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/cost/CastFromHandForFreeEffect.java @@ -0,0 +1,48 @@ +package mage.abilities.effects.common.cost; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * @author fireshoes - Original Code + * @author JRHerlehy - Implement as seperate class + *

+ * Allows player to choose to cast as card from hand without paying its mana + * cost. + *

+ */ +public class CastFromHandForFreeEffect extends OneShotEffect { + + private final FilterCard filter; + + public CastFromHandForFreeEffect(FilterCard filter) { + super(Outcome.PlayForFree); + this.filter = filter; + this.staticText = "you may cast " + filter.getMessage() + " from your hand without paying its mana cost"; + } + + public CastFromHandForFreeEffect(final CastFromHandForFreeEffect effect) { + super(effect); + this.filter = effect.filter; + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + return CardUtil.castSpellWithAttributesForFree(controller, source, game, new CardsImpl(controller.getHand()), filter); + } + + @Override + public CastFromHandForFreeEffect copy() { + return new CastFromHandForFreeEffect(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/cost/CastWithoutPayingManaCostEffect.java b/Mage/src/main/java/mage/abilities/effects/common/cost/CastWithoutPayingManaCostEffect.java deleted file mode 100644 index a25e98fd522..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/cost/CastWithoutPayingManaCostEffect.java +++ /dev/null @@ -1,115 +0,0 @@ -package mage.abilities.effects.common.cost; - -import mage.ApprovingObject; -import mage.abilities.Ability; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.StaticValue; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.constants.ComparisonType; -import mage.constants.Outcome; -import mage.filter.FilterCard; -import mage.filter.common.FilterNonlandCard; -import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.Game; -import mage.players.Player; -import mage.target.Target; -import mage.target.common.TargetCardInHand; -import mage.util.CardUtil; -import org.apache.log4j.Logger; - -/** - * @author fireshoes - Original Code - * @author JRHerlehy - Implement as seperate class - *

- * Allows player to choose to cast as card from hand without paying its mana - * cost. - *

- * TODO: this doesn't work correctly with MDFCs or Adventures (see https://github.com/magefree/mage/issues/7742) - */ -public class CastWithoutPayingManaCostEffect extends OneShotEffect { - - private final DynamicValue manaCost; - private final FilterCard filter; - private static final FilterCard defaultFilter - = new FilterNonlandCard("card with mana value %mv or less from your hand"); - - /** - * @param maxCost Maximum converted mana cost for this effect to apply to - */ - public CastWithoutPayingManaCostEffect(int maxCost) { - this(StaticValue.get(maxCost)); - } - - public CastWithoutPayingManaCostEffect(DynamicValue maxCost) { - this(maxCost, defaultFilter); - } - - public CastWithoutPayingManaCostEffect(DynamicValue maxCost, FilterCard filter) { - super(Outcome.PlayForFree); - this.manaCost = maxCost; - this.filter = filter; - this.staticText = "you may cast a spell with mana value " - + maxCost + " or less from your hand without paying its mana cost"; - } - - public CastWithoutPayingManaCostEffect(final CastWithoutPayingManaCostEffect effect) { - super(effect); - this.manaCost = effect.manaCost; - this.filter = effect.filter; - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - int cmc = manaCost.calculate(game, source, this); - FilterCard filter = this.filter.copy(); - filter.setMessage(filter.getMessage().replace("%mv", "" + cmc)); - filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, cmc + 1)); - Target target = new TargetCardInHand(filter); - if (!target.canChoose( - source.getSourceId(), controller.getId(), game - ) || !controller.chooseUse( - Outcome.PlayForFree, - "Cast " + CardUtil.addArticle(filter.getMessage()) - + " without paying its mana cost?", source, game - )) { - return true; - } - Card cardToCast = null; - boolean cancel = false; - while (controller.canRespond() - && !cancel) { - if (controller.chooseTarget(Outcome.PlayForFree, target, source, game)) { - cardToCast = game.getCard(target.getFirstTarget()); - if (cardToCast != null) { - if (cardToCast.getSpellAbility() == null) { - Logger.getLogger(CastWithoutPayingManaCostEffect.class).fatal("Card: " - + cardToCast.getName() + " is no land and has no spell ability!"); - cancel = true; - } - if (cardToCast.getSpellAbility().canChooseTarget(game, controller.getId())) { - cancel = true; - } - } - } else { - cancel = true; - } - } - if (cardToCast != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(cardToCast, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + cardToCast.getId(), null); - } - return true; - } - - @Override - public CastWithoutPayingManaCostEffect copy() { - return new CastWithoutPayingManaCostEffect(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java b/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java index 67a8b94bfd6..3d39e5b92b5 100644 --- a/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/AftermathAbility.java @@ -6,8 +6,7 @@ import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.ReplacementEffectImpl; import mage.cards.Card; -import mage.cards.ModalDoubleFacesCardHalf; -import mage.cards.SplitCardHalf; +import mage.cards.SubCard; import mage.constants.AsThoughEffectType; import mage.constants.Duration; import mage.constants.Outcome; @@ -156,12 +155,8 @@ class AftermathExileAsResolvesFromGraveyard extends ReplacementEffectImpl { // wants to do that in the future. UUID sourceId = source.getSourceId(); Card sourceCard = game.getCard(source.getSourceId()); - if (sourceCard instanceof SplitCardHalf) { - sourceCard = ((SplitCardHalf) sourceCard).getParentCard(); - sourceId = sourceCard.getId(); - } - if (sourceCard instanceof ModalDoubleFacesCardHalf) { - sourceCard = ((ModalDoubleFacesCardHalf) sourceCard).getParentCard(); + if (sourceCard instanceof SubCard) { + sourceCard = ((SubCard) sourceCard).getParentCard(); sourceId = sourceCard.getId(); } @@ -178,11 +173,8 @@ class AftermathExileAsResolvesFromGraveyard extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { Card sourceCard = game.getCard(source.getSourceId()); - if (sourceCard instanceof SplitCardHalf) { - sourceCard = ((SplitCardHalf) sourceCard).getParentCard(); - } - if (sourceCard instanceof ModalDoubleFacesCardHalf) { - sourceCard = ((ModalDoubleFacesCardHalf) sourceCard).getParentCard(); + if (sourceCard instanceof SubCard) { + sourceCard = ((SubCard) sourceCard).getParentCard(); } if (sourceCard != null) { Player player = game.getPlayer(sourceCard.getOwnerId()); diff --git a/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java b/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java index 3e459f37658..c8724ab1680 100644 --- a/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/CascadeAbility.java @@ -1,23 +1,23 @@ package mage.abilities.keyword; -import mage.ApprovingObject; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; -import mage.cards.*; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.ComparisonType; import mage.constants.Outcome; import mage.constants.Zone; +import mage.filter.FilterCard; import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.players.Player; import mage.target.common.TargetCardInExile; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; +import mage.util.CardUtil; /** * Cascade A keyword ability that may let a player cast a random extra spell for @@ -152,45 +152,12 @@ class CascadeEffect extends OneShotEffect { } // You may cast that spell without paying its mana cost if its converted mana cost is less than this spell's converted mana cost. - List partsToCast = new ArrayList<>(); - if (cardToCast != null) { - if (cardToCast instanceof SplitCard) { - partsToCast.add(((SplitCard) cardToCast).getLeftHalfCard()); - partsToCast.add(((SplitCard) cardToCast).getRightHalfCard()); - partsToCast.add(cardToCast); - } else if (cardToCast instanceof AdventureCard) { - partsToCast.add(((AdventureCard) cardToCast).getSpellCard()); - partsToCast.add(cardToCast); - } else if (cardToCast instanceof ModalDoubleFacesCard) { - partsToCast.add(((ModalDoubleFacesCard) cardToCast).getLeftHalfCard()); - partsToCast.add(((ModalDoubleFacesCard) cardToCast).getRightHalfCard()); - } else { - partsToCast.add(cardToCast); - } - // remove too big cmc - partsToCast.removeIf(card -> card.getManaValue() >= sourceCost); - // remove non spells - partsToCast.removeIf(card -> card.getSpellAbility() == null); - } - - String partsInfo = partsToCast.stream() - .map(MageObject::getIdName) - .collect(Collectors.joining(" or ")); - if (cardToCast != null - && partsToCast.size() > 0 - && controller.chooseUse(outcome, "Cast spell without paying its mana cost (" + partsInfo + ")?", source, game)) { - try { - // enable free cast for all compatible parts - partsToCast.forEach(card -> game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE)); - controller.cast(controller.chooseAbilityForCast(cardToCast, game, true), - game, true, new ApprovingObject(source, game)); - } finally { - partsToCast.forEach(card -> game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null)); - } - } + FilterCard filter = new FilterCard(); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, sourceCost + 1)); + CardUtil.castSpellWithAttributesForFree(controller, source, game, new CardsImpl(cardToCast), filter); // Then put all cards exiled this way that weren't cast on the bottom of your library in a random order. - cardsToExile.removeIf(uuid -> game.getState().getZone(uuid) != Zone.EXILED); + cardsToExile.retainZone(Zone.EXILED, game); return controller.putCardsOnBottomOfLibrary(cardsToExile, game, source, false); } diff --git a/Mage/src/main/java/mage/cards/AdventureCard.java b/Mage/src/main/java/mage/cards/AdventureCard.java index bc3bab17758..3f556598b9d 100644 --- a/Mage/src/main/java/mage/cards/AdventureCard.java +++ b/Mage/src/main/java/mage/cards/AdventureCard.java @@ -5,6 +5,7 @@ import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; import mage.abilities.SpellAbility; import mage.constants.CardType; +import mage.constants.SpellAbilityType; import mage.constants.Zone; import mage.game.Game; import mage.game.events.ZoneChangeEvent; @@ -13,7 +14,7 @@ import java.util.List; import java.util.UUID; /** - * @author TheElk801 + * @author phulin */ public abstract class AdventureCard extends CardImpl { @@ -90,13 +91,11 @@ public abstract class AdventureCard extends CardImpl { @Override public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) { - switch (ability.getSpellAbilityType()) { - case ADVENTURE_SPELL: - return this.getSpellCard().cast(game, fromZone, ability, controllerId); - default: - this.getSpellCard().getSpellAbility().setControllerId(controllerId); - return super.cast(game, fromZone, ability, controllerId); + if (ability.getSpellAbilityType() == SpellAbilityType.ADVENTURE_SPELL) { + return this.getSpellCard().cast(game, fromZone, ability, controllerId); } + this.getSpellCard().getSpellAbility().setControllerId(controllerId); + return super.cast(game, fromZone, ability, controllerId); } @Override diff --git a/Mage/src/main/java/mage/cards/AdventureCardSpell.java b/Mage/src/main/java/mage/cards/AdventureCardSpell.java index 6e75792c949..c274c54aa9a 100644 --- a/Mage/src/main/java/mage/cards/AdventureCardSpell.java +++ b/Mage/src/main/java/mage/cards/AdventureCardSpell.java @@ -1,15 +1,10 @@ package mage.cards; /** - * * @author phulin */ -public interface AdventureCardSpell extends Card { +public interface AdventureCardSpell extends SubCard { @Override AdventureCardSpell copy(); - - void setParentCard(AdventureCard card); - - AdventureCard getParentCard(); } diff --git a/Mage/src/main/java/mage/cards/CardWithHalves.java b/Mage/src/main/java/mage/cards/CardWithHalves.java new file mode 100644 index 00000000000..a37c6c1a6e3 --- /dev/null +++ b/Mage/src/main/java/mage/cards/CardWithHalves.java @@ -0,0 +1,11 @@ +package mage.cards; + +/** + * @author TheElk801 + */ +public interface CardWithHalves extends Card { + + Card getLeftHalfCard(); + + Card getRightHalfCard(); +} diff --git a/Mage/src/main/java/mage/cards/Cards.java b/Mage/src/main/java/mage/cards/Cards.java index 44e0ea223da..388e985f119 100644 --- a/Mage/src/main/java/mage/cards/Cards.java +++ b/Mage/src/main/java/mage/cards/Cards.java @@ -45,4 +45,6 @@ public interface Cards extends Set, Serializable { Cards copy(); void retainZone(Zone zone, Game game); + + void removeZone(Zone zone, Game game); } diff --git a/Mage/src/main/java/mage/cards/CardsImpl.java b/Mage/src/main/java/mage/cards/CardsImpl.java index b0f45e4ab9c..a3fcd217d02 100644 --- a/Mage/src/main/java/mage/cards/CardsImpl.java +++ b/Mage/src/main/java/mage/cards/CardsImpl.java @@ -210,4 +210,9 @@ public class CardsImpl extends LinkedHashSet implements Cards, Serializabl public void retainZone(Zone zone, Game game) { removeIf(uuid -> game.getState().getZone(uuid) != zone); } + + @Override + public void removeZone(Zone zone, Game game) { + removeIf(uuid -> game.getState().getZone(uuid) == zone); + } } diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java b/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java index db7062b9313..b5ab8f42828 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacesCard.java @@ -22,7 +22,7 @@ import java.util.UUID; /** * @author JayDi85 */ -public abstract class ModalDoubleFacesCard extends CardImpl { +public abstract class ModalDoubleFacesCard extends CardImpl implements CardWithHalves { protected Card leftHalfCard; // main card in all zone protected Card rightHalfCard; // second side card, can be only in stack and battlefield zones diff --git a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java index d7dee2090bd..7ebbf338fd8 100644 --- a/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java +++ b/Mage/src/main/java/mage/cards/ModalDoubleFacesCardHalf.java @@ -5,15 +5,11 @@ import mage.MageInt; /** * @author JayDi85 */ -public interface ModalDoubleFacesCardHalf extends Card { +public interface ModalDoubleFacesCardHalf extends SubCard { @Override ModalDoubleFacesCardHalf copy(); - void setParentCard(ModalDoubleFacesCard card); - - ModalDoubleFacesCard getParentCard(); - void setPT(int power, int toughness); void setPT(MageInt power, MageInt toughness); diff --git a/Mage/src/main/java/mage/cards/SplitCard.java b/Mage/src/main/java/mage/cards/SplitCard.java index b1d0c9a1763..36e799b21ac 100644 --- a/Mage/src/main/java/mage/cards/SplitCard.java +++ b/Mage/src/main/java/mage/cards/SplitCard.java @@ -18,7 +18,7 @@ import java.util.UUID; /** * @author LevelX2 */ -public abstract class SplitCard extends CardImpl { +public abstract class SplitCard extends CardImpl implements CardWithHalves { protected Card leftHalfCard; protected Card rightHalfCard; diff --git a/Mage/src/main/java/mage/cards/SplitCardHalf.java b/Mage/src/main/java/mage/cards/SplitCardHalf.java index 76db56ce603..957d97999f9 100644 --- a/Mage/src/main/java/mage/cards/SplitCardHalf.java +++ b/Mage/src/main/java/mage/cards/SplitCardHalf.java @@ -3,12 +3,8 @@ package mage.cards; /** * @author LevelX2 */ -public interface SplitCardHalf extends Card { +public interface SplitCardHalf extends SubCard { @Override SplitCardHalf copy(); - - void setParentCard(SplitCard card); - - SplitCard getParentCard(); } diff --git a/Mage/src/main/java/mage/cards/SubCard.java b/Mage/src/main/java/mage/cards/SubCard.java new file mode 100644 index 00000000000..92127541bb8 --- /dev/null +++ b/Mage/src/main/java/mage/cards/SubCard.java @@ -0,0 +1,8 @@ +package mage.cards; + +public interface SubCard extends Card { + + void setParentCard(T card); + + T getParentCard(); +} diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java index cb89353be36..e88d60bd304 100644 --- a/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/NamePredicate.java @@ -1,7 +1,7 @@ package mage.filter.predicate.mageobject; import mage.MageObject; -import mage.cards.ModalDoubleFacesCard; +import mage.cards.CardWithHalves; import mage.cards.SplitCard; import mage.constants.SpellAbilityType; import mage.filter.predicate.Predicate; @@ -34,13 +34,9 @@ public class NamePredicate implements Predicate { // 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 SplitCard) { - return CardUtil.haveSameNames(name, ((SplitCard) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || - CardUtil.haveSameNames(name, ((SplitCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || - CardUtil.haveSameNames(name, input.getName(), this.ignoreMtgRuleForEmptyNames); - } else if (input instanceof ModalDoubleFacesCard) { - return CardUtil.haveSameNames(name, ((ModalDoubleFacesCard) input).getLeftHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || - CardUtil.haveSameNames(name, ((ModalDoubleFacesCard) input).getRightHalfCard().getName(), this.ignoreMtgRuleForEmptyNames) || + if (input instanceof CardWithHalves) { + 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); } else if (input instanceof Spell && ((Spell) input).getSpellAbility().getSpellAbilityType() == SpellAbilityType.SPLIT_FUSED) { SplitCard card = (SplitCard) ((Spell) input).getCard(); diff --git a/Mage/src/main/java/mage/game/command/emblems/KayaTheInexorableEmblem.java b/Mage/src/main/java/mage/game/command/emblems/KayaTheInexorableEmblem.java index 96b753831dd..598351cb342 100644 --- a/Mage/src/main/java/mage/game/command/emblems/KayaTheInexorableEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/KayaTheInexorableEmblem.java @@ -1,48 +1,51 @@ package mage.game.command.emblems; -import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; import mage.choices.Choice; import mage.choices.ChoiceImpl; -import mage.constants.*; +import mage.constants.Outcome; +import mage.constants.SuperType; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.FilterCard; import mage.filter.common.FilterOwnedCard; -import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.command.Emblem; import mage.players.Player; -import mage.target.TargetCard; -import mage.target.common.TargetCardInExile; -import mage.target.common.TargetCardInHand; -import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; import java.util.LinkedHashSet; import java.util.Set; /** - * * @author weirddan455 */ -public class KayaTheInexorableEmblem extends Emblem { +public class KayaTheInexorableEmblem extends Emblem { // −7: You get an emblem with "At the beginning of your upkeep, you may cast a legendary spell from your hand, from your graveyard, or from among cards you own in exile without paying its mana cost." public KayaTheInexorableEmblem() { this.setName("Emblem Kaya"); this.setExpansionSetCodeForImage("KHM"); - this.getAbilities().add(new BeginningOfUpkeepTriggeredAbility(Zone.COMMAND, new KayaTheInexorableEmblemEffect(), TargetController.YOU, true, false)); + this.getAbilities().add(new BeginningOfUpkeepTriggeredAbility( + Zone.COMMAND, new KayaTheInexorableEmblemEffect(), + TargetController.YOU, true, false + )); } } class KayaTheInexorableEmblemEffect extends OneShotEffect { - private static final FilterOwnedCard filter = new FilterOwnedCard(); + private static final FilterCard filter = new FilterOwnedCard(); + private static final FilterCard filter2 = new FilterCard(); private static final Set choices = new LinkedHashSet<>(); + static { - filter.add(SuperType.LEGENDARY.getPredicate()); - filter.add(Predicates.not(CardType.LAND.getPredicate())); + filter2.add(SuperType.LEGENDARY.getPredicate()); choices.add("Hand"); choices.add("Graveyard"); choices.add("Exile"); @@ -50,7 +53,8 @@ class KayaTheInexorableEmblemEffect extends OneShotEffect { public KayaTheInexorableEmblemEffect() { super(Outcome.PlayForFree); - this.staticText = "cast a legendary spell from your hand, from your graveyard, or from among cards you own in exile without paying its mana cost"; + this.staticText = "cast a legendary spell from your hand, from your graveyard, " + + "or from among cards you own in exile without paying its mana cost"; } private KayaTheInexorableEmblemEffect(final KayaTheInexorableEmblemEffect effect) { @@ -65,40 +69,26 @@ class KayaTheInexorableEmblemEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - Choice zoneChoice = new ChoiceImpl(true); - zoneChoice.setMessage("Cast a legendary spell from hand, graveyard, or exile"); - zoneChoice.setChoices(choices); - zoneChoice.clearChoice(); - if (player.choose(Outcome.PlayForFree, zoneChoice, game)) { - TargetCard target = null; - switch (zoneChoice.getChoice()) { - case "Hand": - target = new TargetCardInHand(0, 1, filter); - target.setTargetName("legendary spell from your hand"); - break; - case "Graveyard": - target = new TargetCardInYourGraveyard(0, 1, filter, true); - target.setTargetName("legendary spell from your graveyard"); - break; - case "Exile": - target = new TargetCardInExile(0, 1, filter, null, true); - target.setNotTarget(true); - target.setTargetName("legendary spell you own in exile"); - break; - } - if (target != null && player.chooseTarget(Outcome.PlayForFree, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - boolean cardWasCast = player.cast(player.chooseAbilityForCast(card, game, true), - game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - return cardWasCast; - } - } - } + if (player == null) { + return false; } - return false; + Choice zoneChoice = new ChoiceImpl(true); + zoneChoice.setMessage("Cast a legendary spell from hand, graveyard, or exile"); + zoneChoice.setChoices(choices); + zoneChoice.clearChoice(); + player.choose(Outcome.PlayForFree, zoneChoice, game); + Cards cards = new CardsImpl(); + switch (zoneChoice.getChoice()) { + case "Hand": + cards.addAll(player.getHand()); + break; + case "Graveyard": + cards.addAll(player.getGraveyard()); + break; + case "Exile": + cards.addAll(game.getExile().getCards(filter, game)); + break; + } + return CardUtil.castSpellWithAttributesForFree(player, source, game, cards, filter2); } } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 51fc3e8602e..3652f04db9f 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -1571,72 +1571,75 @@ public abstract class PlayerImpl implements Player, Serializable { * @param noMana * @return */ - public static LinkedHashMap getCastableSpellAbilities(Game game, UUID playerId, MageObject object, Zone zone, boolean noMana) { + public static LinkedHashMap getCastableSpellAbilities(Game game, UUID playerId, MageObject object, Zone zone, boolean noMana) { // it uses simple check from spellCanBeActivatedRegularlyNow // reason: no approved info here (e.g. forced to choose spell ability from cast card) - LinkedHashMap useable = new LinkedHashMap<>(); + LinkedHashMap useable = new LinkedHashMap<>(); Abilities allAbilities; if (object instanceof Card) { allAbilities = ((Card) object).getAbilities(game); } else { allAbilities = object.getAbilities(); } - - for (Ability ability : allAbilities) { - if (ability instanceof SpellAbility) { - SpellAbility spellAbility = (SpellAbility) ability; - - switch (spellAbility.getSpellAbilityType()) { - case BASE_ALTERNATE: - // rules: - // If you cast a spell “without paying its mana cost,” you can’t choose to cast it for - // any alternative costs. You can, however, pay additional costs, such as kicker costs. - // If the card has any mandatory additional costs, those must be paid to cast the spell. - // (2021-02-05) - if (!noMana) { - if (spellAbility.spellCanBeActivatedRegularlyNow(playerId, game)) { - useable.put(spellAbility.getId(), spellAbility); // example: Chandra, Torch of Defiance +1 loyal ability - } - return useable; + for (SpellAbility spellAbility : allAbilities + .stream() + .filter(SpellAbility.class::isInstance) + .map(SpellAbility.class::cast) + .collect(Collectors.toList())) { + switch (spellAbility.getSpellAbilityType()) { + case BASE_ALTERNATE: + // rules: + // If you cast a spell “without paying its mana cost,” you can’t choose to cast it for + // any alternative costs. You can, however, pay additional costs, such as kicker costs. + // If the card has any mandatory additional costs, those must be paid to cast the spell. + // (2021-02-05) + if (!noMana) { + if (spellAbility.spellCanBeActivatedRegularlyNow(playerId, game)) { + useable.put(spellAbility.getId(), spellAbility); // example: Chandra, Torch of Defiance +1 loyal ability } - break; - case SPLIT_FUSED: - // rules: - // If you cast a split card with fuse from your hand without paying its mana cost, - // you can choose to use its fuse ability and cast both halves without paying their mana costs. - if (zone == Zone.HAND) { - if (spellAbility.canChooseTarget(game, playerId)) { - useable.put(spellAbility.getId(), spellAbility); - } - } - case SPLIT: - if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game, playerId)) { - useable.put(((SplitCard) object).getLeftHalfCard().getSpellAbility().getId(), - ((SplitCard) object).getLeftHalfCard().getSpellAbility()); + return useable; + } + break; + case SPLIT_FUSED: + // rules: + // If you cast a split card with fuse from your hand without paying its mana cost, + // you can choose to use its fuse ability and cast both halves without paying their mana costs. + if (zone == Zone.HAND) { + if (spellAbility.canChooseTarget(game, playerId)) { + useable.put(spellAbility.getId(), spellAbility); } + } + case SPLIT: + if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game, playerId)) { + useable.put( + ((SplitCard) object).getLeftHalfCard().getSpellAbility().getId(), + ((SplitCard) object).getLeftHalfCard().getSpellAbility() + ); + } + if (((SplitCard) object).getRightHalfCard().getSpellAbility().canChooseTarget(game, playerId)) { + useable.put( + ((SplitCard) object).getRightHalfCard().getSpellAbility().getId(), + ((SplitCard) object).getRightHalfCard().getSpellAbility() + ); + } + return useable; + case SPLIT_AFTERMATH: + if (zone == Zone.GRAVEYARD) { if (((SplitCard) object).getRightHalfCard().getSpellAbility().canChooseTarget(game, playerId)) { useable.put(((SplitCard) object).getRightHalfCard().getSpellAbility().getId(), ((SplitCard) object).getRightHalfCard().getSpellAbility()); } - return useable; - case SPLIT_AFTERMATH: - if (zone == Zone.GRAVEYARD) { - if (((SplitCard) object).getRightHalfCard().getSpellAbility().canChooseTarget(game, playerId)) { - useable.put(((SplitCard) object).getRightHalfCard().getSpellAbility().getId(), - ((SplitCard) object).getRightHalfCard().getSpellAbility()); - } - } else { - if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game, playerId)) { - useable.put(((SplitCard) object).getLeftHalfCard().getSpellAbility().getId(), - ((SplitCard) object).getLeftHalfCard().getSpellAbility()); - } + } else { + if (((SplitCard) object).getLeftHalfCard().getSpellAbility().canChooseTarget(game, playerId)) { + useable.put(((SplitCard) object).getLeftHalfCard().getSpellAbility().getId(), + ((SplitCard) object).getLeftHalfCard().getSpellAbility()); } - return useable; - default: - if (spellAbility.spellCanBeActivatedRegularlyNow(playerId, game)) { - useable.put(spellAbility.getId(), spellAbility); - } - } + } + return useable; + default: + if (spellAbility.spellCanBeActivatedRegularlyNow(playerId, game)) { + useable.put(spellAbility.getId(), spellAbility); + } } } return useable; diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index 4ef5a534072..c618f2e1a55 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1,6 +1,7 @@ package mage.util; import com.google.common.collect.ImmutableList; +import mage.ApprovingObject; import mage.MageObject; import mage.Mana; import mage.abilities.Abilities; @@ -23,6 +24,7 @@ import mage.cards.*; import mage.constants.*; import mage.counters.Counter; import mage.filter.Filter; +import mage.filter.FilterCard; import mage.filter.predicate.mageobject.NamePredicate; import mage.game.CardState; import mage.game.Game; @@ -36,6 +38,7 @@ import mage.game.permanent.token.Token; import mage.game.stack.Spell; import mage.players.Player; import mage.target.Target; +import mage.target.TargetCard; import mage.target.targetpointer.FixedTarget; import mage.util.functions.CopyTokenFunction; import org.apache.log4j.Logger; @@ -1196,6 +1199,126 @@ public final class CardUtil { } } + public interface SpellCastTracker { + + boolean checkCard(Card card, Game game); + + void addCard(Card card, Ability source, Game game); + } + + private static List getCastableComponents(Card cardToCast, FilterCard filter, UUID sourceId, UUID playerId, Game game, SpellCastTracker spellCastTracker) { + List cards = new ArrayList<>(); + if (cardToCast instanceof CardWithHalves) { + cards.add(((CardWithHalves) cardToCast).getLeftHalfCard()); + cards.add(((CardWithHalves) cardToCast).getRightHalfCard()); + } else if (cardToCast instanceof AdventureCard) { + cards.add(cardToCast); + cards.add(((AdventureCard) cardToCast).getSpellCard()); + } else { + cards.add(cardToCast); + } + cards.removeIf(Objects::isNull); + cards.removeIf(card -> !filter.match(card, sourceId, playerId, game)); + if (spellCastTracker != null) { + cards.removeIf(card -> spellCastTracker.checkCard(card, game)); + } + return cards; + } + + private static final FilterCard defaultFilter = new FilterCard("card to cast"); + + public static boolean castSpellWithAttributesForFree(Player player, Ability source, Game game, Cards cards, FilterCard filter) { + return castSpellWithAttributesForFree(player, source, game, cards, filter, null); + } + + public static boolean castSpellWithAttributesForFree(Player player, Ability source, Game game, Cards cards, FilterCard filter, SpellCastTracker spellCastTracker) { + Map> cardMap = new HashMap<>(); + for (Card card : cards.getCards(game)) { + List castableComponents = getCastableComponents(card, filter, source.getSourceId(), player.getId(), game, spellCastTracker); + if (!castableComponents.isEmpty()) { + cardMap.put(card.getId(), castableComponents); + } + } + Card cardToCast; + switch (cardMap.size()) { + case 0: + return false; + case 1: + cardToCast = cards.get(cardMap.keySet().stream().findFirst().orElse(null), game); + break; + default: + Cards castableCards = new CardsImpl(cardMap.keySet()); + TargetCard target = new TargetCard(0, 1, Zone.ALL, defaultFilter); + target.setNotTarget(true); + player.choose(Outcome.PlayForFree, castableCards, target, game); + cardToCast = castableCards.get(target.getFirstTarget(), game); + } + if (cardToCast == null) { + return false; + } + List partsToCast = cardMap.get(cardToCast.getId()); + String partsInfo = partsToCast + .stream() + .map(MageObject::getIdName) + .collect(Collectors.joining(" or ")); + if (cardToCast == null + || partsToCast.size() < 1 + || !player.chooseUse( + Outcome.PlayForFree, "Cast spell without paying its mana cost (" + partsInfo + ")?", source, game + )) { + return false; + } + partsToCast.forEach(card -> game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE)); + boolean result = player.cast( + player.chooseAbilityForCast(cardToCast, game, true), + game, true, new ApprovingObject(source, game) + ); + partsToCast.forEach(card -> game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null)); + if (result && spellCastTracker != null) { + spellCastTracker.addCard(cardToCast, source, game); + } + if (player.isComputer() && !result) { + cards.remove(cardToCast); + } + return result; + } + + private static boolean checkForPlayable(Cards cards, FilterCard filter, UUID sourceId, UUID playerId, Game game, SpellCastTracker spellCastTracker) { + return cards + .getCards(game) + .stream() + .anyMatch(card -> !getCastableComponents(card, filter, sourceId, playerId, game, spellCastTracker).isEmpty()); + } + + public static void castMultipleWithAttributeForFree(Player player, Ability source, Game game, Cards cards, FilterCard filter) { + castMultipleWithAttributeForFree(player, source, game, cards, filter, Integer.MAX_VALUE); + } + + public static void castMultipleWithAttributeForFree(Player player, Ability source, Game game, Cards cards, FilterCard filter, int maxSpells) { + castMultipleWithAttributeForFree(player, source, game, cards, filter, maxSpells, null); + } + + public static void castMultipleWithAttributeForFree(Player player, Ability source, Game game, Cards cards, FilterCard filter, int maxSpells, SpellCastTracker spellCastTracker) { + if (maxSpells == 1) { + CardUtil.castSpellWithAttributesForFree(player, source, game, cards, filter); + return; + } + int spellsCast = 0; + cards.removeZone(Zone.STACK, game); + while (player.canRespond() && spellsCast < maxSpells && !cards.isEmpty()) { + if (CardUtil.castSpellWithAttributesForFree(player, source, game, cards, filter, spellCastTracker)) { + spellsCast++; + cards.removeZone(Zone.STACK, game); + } else if (!checkForPlayable( + cards, filter, source.getSourceId(), player.getId(), game, spellCastTracker + ) || !player.chooseUse( + Outcome.PlayForFree, "Continue casting spells?", source, game + )) { + break; + } + } + } + /** * Pay life in effects * diff --git a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java index 94f222340a4..24acfb71ca4 100644 --- a/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java +++ b/Mage/src/main/java/mage/watchers/common/SpellsCastWatcher.java @@ -13,6 +13,7 @@ import mage.game.stack.Spell; import mage.watchers.Watcher; import java.util.*; +import java.util.stream.Stream; /** * @author LevelX2 @@ -59,6 +60,10 @@ public class SpellsCastWatcher extends Watcher { spellsCastFromGraveyard.clear(); } + public Stream getAllSpellsCastThisTurn() { + return spellsCast.values().stream().flatMap(Collection::stream); + } + public List getSpellsCastThisTurn(UUID playerId) { return spellsCast.computeIfAbsent(playerId, x -> new ArrayList<>()); }