diff --git a/Mage.Sets/src/mage/cards/a/AnrakyrTheTraveller.java b/Mage.Sets/src/mage/cards/a/AnrakyrTheTraveller.java index 640901b32f7..8cfa165f755 100644 --- a/Mage.Sets/src/mage/cards/a/AnrakyrTheTraveller.java +++ b/Mage.Sets/src/mage/cards/a/AnrakyrTheTraveller.java @@ -4,7 +4,6 @@ import mage.ApprovingObject; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.ActivatedAbility; import mage.abilities.SpellAbility; import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.costs.Cost; @@ -74,8 +73,8 @@ class AnrakyrTheTravellerEffect extends OneShotEffect { return false; } - Set cards = player.getHand().getCards(filter, game); - cards.addAll(player.getGraveyard().getCards(filter, game)); + Set cards = player.getHand().getCards(filter, source.getControllerId(), source, game); + cards.addAll(player.getGraveyard().getCards(filter, source.getControllerId(), source, game)); Map> cardMap = new HashMap<>(); for (Card card : cards) { @@ -103,11 +102,11 @@ class AnrakyrTheTravellerEffect extends OneShotEffect { .stream() .map(MageObject::getLogName) .collect(Collectors.joining(" or ")); - if (partsToCast.size() < 1 + if (partsToCast.isEmpty() || !player.chooseUse( Outcome.PlayForFree, "Cast spell by paying life equal to its mana value rather than paying its mana cost (" + partsInfo + ")?", source, game )) { - return false; + return true; } partsToCast.forEach(card -> game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE)); @@ -119,12 +118,12 @@ class AnrakyrTheTravellerEffect extends OneShotEffect { newCosts.addAll(cardToCast.getSpellAbility().getCosts()); player.setCastSourceIdWithAlternateMana(cardToCast.getId(), null, newCosts); - ActivatedAbility chosenAbility; + SpellAbility chosenAbility; chosenAbility = player.chooseAbilityForCast(cardToCast, game, true); boolean result = false; - if (chosenAbility instanceof SpellAbility) { + if (chosenAbility != null) { result = player.cast( - (SpellAbility) chosenAbility, + chosenAbility, game, true, new ApprovingObject(source, game) ); } @@ -139,4 +138,4 @@ class AnrakyrTheTravellerEffect extends OneShotEffect { public AnrakyrTheTravellerEffect copy() { return new AnrakyrTheTravellerEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java b/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java index 0e3801e0ad3..8880df9a4eb 100644 --- a/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java +++ b/Mage.Sets/src/mage/cards/b/BeseechTheQueen.java @@ -7,7 +7,7 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.filter.FilterCard; -import mage.filter.predicate.card.CardManaCostLessThanControlledLandCountPredicate; +import mage.filter.predicate.card.ManaValueLessThanControlledLandCountPredicate; import mage.target.common.TargetCardInLibrary; /** @@ -18,7 +18,7 @@ public final class BeseechTheQueen extends CardImpl { private static final FilterCard filter = new FilterCard("card with mana value less than or equal to the number of lands you control"); static { - filter.add(CardManaCostLessThanControlledLandCountPredicate.getInstance()); + filter.add(ManaValueLessThanControlledLandCountPredicate.instance); } public BeseechTheQueen(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Sets/src/mage/cards/d/DakkonShadowSlayer.java b/Mage.Sets/src/mage/cards/d/DakkonShadowSlayer.java index 8a8842c20e6..683454f41a2 100644 --- a/Mage.Sets/src/mage/cards/d/DakkonShadowSlayer.java +++ b/Mage.Sets/src/mage/cards/d/DakkonShadowSlayer.java @@ -5,7 +5,7 @@ import mage.abilities.LoyaltyAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.dynamicvalue.common.LandsYouControlCount; import mage.abilities.effects.common.ExileTargetEffect; -import mage.abilities.effects.common.PutCardFromOneOfTwoZonesOntoBattlefieldEffect; +import mage.abilities.effects.common.PutCardFromHandOrGraveyardOntoBattlefieldEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.keyword.SurveilEffect; import mage.abilities.hint.common.LandsYouControlHint; @@ -46,7 +46,7 @@ public final class DakkonShadowSlayer extends CardImpl { this.addAbility(ability); // −6: You may put an artifact card from your hand or graveyard onto the battlefield. - this.addAbility(new LoyaltyAbility(new PutCardFromOneOfTwoZonesOntoBattlefieldEffect(StaticFilters.FILTER_CARD_ARTIFACT), -6)); + this.addAbility(new LoyaltyAbility(new PutCardFromHandOrGraveyardOntoBattlefieldEffect(StaticFilters.FILTER_CARD_ARTIFACT, false), -6)); } private DakkonShadowSlayer(final DakkonShadowSlayer card) { diff --git a/Mage.Sets/src/mage/cards/d/DanithaBenaliasHope.java b/Mage.Sets/src/mage/cards/d/DanithaBenaliasHope.java index 9193a5c64f1..c2fdc0e4c02 100644 --- a/Mage.Sets/src/mage/cards/d/DanithaBenaliasHope.java +++ b/Mage.Sets/src/mage/cards/d/DanithaBenaliasHope.java @@ -59,9 +59,9 @@ public final class DanithaBenaliasHope extends CardImpl { class DanithaBenaliasHopeEffect extends OneShotEffect { - public DanithaBenaliasHopeEffect() { + DanithaBenaliasHopeEffect() { super(Outcome.PutCardInPlay); - this.staticText = "put an Aura or Equipment card from your hand or graveyard onto the battlefield attached to Danitha"; + this.staticText = "you may put an Aura or Equipment card from your hand or graveyard onto the battlefield attached to {this}"; } private DanithaBenaliasHopeEffect(final DanithaBenaliasHopeEffect effect) { @@ -88,9 +88,12 @@ class DanithaBenaliasHopeEffect extends OneShotEffect { SubType.EQUIPMENT.getPredicate() )); Cards cards = new CardsImpl(); - cards.addAllCards(controller.getHand().getCards(filter, game)); - cards.addAllCards(controller.getGraveyard().getCards(filter, game)); - TargetCard target = new TargetCard(Zone.ALL, filter); + cards.addAllCards(controller.getHand().getCards(filter, source.getControllerId(), source, game)); + cards.addAllCards(controller.getGraveyard().getCards(filter, source.getControllerId(), source, game)); + if (cards.isEmpty()) { + return true; + } + TargetCard target = new TargetCard(0, 1, Zone.ALL, filter); target.withNotTarget(true); target.withChooseHint("to attach to " + sourcePermanentName); controller.choose(outcome, cards, target, source, game); diff --git a/Mage.Sets/src/mage/cards/f/FiresOfInvention.java b/Mage.Sets/src/mage/cards/f/FiresOfInvention.java index 7cc79d35271..9185d0eb268 100644 --- a/Mage.Sets/src/mage/cards/f/FiresOfInvention.java +++ b/Mage.Sets/src/mage/cards/f/FiresOfInvention.java @@ -1,6 +1,5 @@ package mage.cards.f; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; @@ -11,9 +10,7 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.filter.FilterCard; -import mage.filter.StaticFilters; -import mage.filter.predicate.ObjectSourcePlayer; -import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.filter.predicate.card.ManaValueLessThanControlledLandCountPredicate; import mage.game.Game; import mage.game.events.GameEvent; import mage.watchers.common.CastSpellLastTurnWatcher; @@ -29,7 +26,7 @@ public final class FiresOfInvention extends CardImpl { = new FilterCard("spells with mana value less than or equal to the number of lands you control"); static { - filter.add(FiresOfInventionPredicate.instance); + filter.add(ManaValueLessThanControlledLandCountPredicate.instance); } public FiresOfInvention(UUID ownerId, CardSetInfo setInfo) { @@ -52,16 +49,6 @@ public final class FiresOfInvention extends CardImpl { } } -enum FiresOfInventionPredicate implements ObjectSourcePlayerPredicate { - instance; - - @Override - public boolean apply(ObjectSourcePlayer input, Game game) { - return input.getObject().getManaValue() <= - game.getBattlefield().countAll(StaticFilters.FILTER_LAND, game.getControllerId(input.getSourceId()), game); - } -} - class FiresOfInventionCastEffect extends ContinuousRuleModifyingEffectImpl { FiresOfInventionCastEffect() { @@ -96,4 +83,4 @@ class FiresOfInventionCastEffect extends ContinuousRuleModifyingEffectImpl { || !game.getActivePlayerId().equals(source.getControllerId()); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/l/LiberatedLivestock.java b/Mage.Sets/src/mage/cards/l/LiberatedLivestock.java index d72c48eaa54..31cf3979652 100644 --- a/Mage.Sets/src/mage/cards/l/LiberatedLivestock.java +++ b/Mage.Sets/src/mage/cards/l/LiberatedLivestock.java @@ -78,8 +78,11 @@ class LiberatedLivestockEffect extends OneShotEffect { filter.add(SubType.AURA.getPredicate()); filter.add(new AuraCardCanAttachToPermanentId(tokenPermanent.getId())); Cards auraCards = new CardsImpl(); - auraCards.addAllCards(controller.getHand().getCards(filter, game)); - auraCards.addAllCards(controller.getGraveyard().getCards(filter, game)); + auraCards.addAllCards(controller.getHand().getCards(filter, source.getControllerId(), source, game)); + auraCards.addAllCards(controller.getGraveyard().getCards(filter, source.getControllerId(), source, game)); + if (auraCards.isEmpty()) { + continue; + } TargetCard target = new TargetCard(0, 1, Zone.ALL, filter); target.withNotTarget(true); controller.chooseTarget(outcome, auraCards, target, source, game); diff --git a/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java b/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java index cfc0d49df0d..725c151e8f3 100644 --- a/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java +++ b/Mage.Sets/src/mage/cards/n/NahiriTheLithomancer.java @@ -8,7 +8,7 @@ import mage.abilities.common.CanBeYourCommanderAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; -import mage.abilities.effects.common.PutCardFromOneOfTwoZonesOntoBattlefieldEffect; +import mage.abilities.effects.common.PutCardFromHandOrGraveyardOntoBattlefieldEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -32,7 +32,7 @@ import mage.target.common.TargetControlledPermanent; */ public final class NahiriTheLithomancer extends CardImpl { - private static final FilterCard filter = new FilterCard("an Equipment card"); + private static final FilterCard filter = new FilterCard("Equipment card"); static { filter.add(SubType.EQUIPMENT.getPredicate()); @@ -49,7 +49,7 @@ public final class NahiriTheLithomancer extends CardImpl { this.addAbility(new LoyaltyAbility(new NahiriTheLithomancerFirstAbilityEffect(), 2)); // -2: You may put an Equipment card from your hand or graveyard onto the battlefield. - this.addAbility(new LoyaltyAbility(new PutCardFromOneOfTwoZonesOntoBattlefieldEffect(filter), -2)); + this.addAbility(new LoyaltyAbility(new PutCardFromHandOrGraveyardOntoBattlefieldEffect(filter, false), -2)); // -10: Create a colorless Equipment artifact token named Stoneforged Blade. It has indestructible, "Equipped creature gets +5/+5 and has double strike," and equip {0}. Effect effect = new CreateTokenEffect(new NahiriTheLithomancerEquipmentToken()); diff --git a/Mage.Sets/src/mage/cards/n/NextOfKin.java b/Mage.Sets/src/mage/cards/n/NextOfKin.java index 9d8f49b6d6b..44b875c7cbd 100644 --- a/Mage.Sets/src/mage/cards/n/NextOfKin.java +++ b/Mage.Sets/src/mage/cards/n/NextOfKin.java @@ -1,23 +1,16 @@ package mage.cards.n; -import mage.Mana; import mage.abilities.Ability; import mage.abilities.common.DiesAttachedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.AttachEffect; -import mage.abilities.effects.common.InfoEffect; -import mage.abilities.effects.common.PutCardFromOneOfTwoZonesOntoBattlefieldEffect; import mage.abilities.effects.common.ReturnToBattlefieldAttachedEffect; import mage.abilities.keyword.EnchantAbility; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.cards.*; import mage.constants.*; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.card.CastFromZonePredicate; import mage.filter.predicate.mageobject.ManaValuePredicate; import mage.game.Game; import mage.game.permanent.Permanent; @@ -25,9 +18,9 @@ import mage.players.Player; import mage.target.TargetCard; import mage.target.TargetPermanent; import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; import java.util.UUID; -import java.util.stream.Collectors; /** * @author Alex-Vasile @@ -62,7 +55,7 @@ public class NextOfKin extends CardImpl { class NextOfKinDiesEffect extends OneShotEffect { NextOfKinDiesEffect() { - super(Outcome.Benefit); + super(Outcome.PutCardInPlay); this.staticText = "you may put a creature card you own with lesser mana value from your hand or from the command zone onto the battlefield. " + "If you do, return {this} to the battlefield attached to that creature at the beginning of the next end step."; } @@ -81,21 +74,28 @@ class NextOfKinDiesEffect extends OneShotEffect { } int manaValue = ((Permanent) object).getManaValue(); - FilterCreatureCard filterCreatureCard = new FilterCreatureCard("a creature card you own with lesser mana value from your hand or from the command zone"); - filterCreatureCard.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, manaValue)); - - // This effect is used only to get the info about which card was added. - Effect hackTargetEffect = new InfoEffect(""); - - Effect putCardEffect = new PutCardFromOneOfTwoZonesOntoBattlefieldEffect(filterCreatureCard, false, hackTargetEffect, Zone.HAND, Zone.COMMAND); - boolean cardPut = putCardEffect.apply(game, source); - if (!cardPut) { - return false; + FilterCreatureCard filter = new FilterCreatureCard("a creature card you own with lesser mana value"); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, manaValue)); + Cards cards = new CardsImpl(); + cards.addAllCards(controller.getHand().getCards(filter, source.getControllerId(), source, game)); + for (Card possibleCard : game.getCommanderCardsFromCommandZone(controller, CommanderCardType.ANY)) { + if (filter.match(possibleCard, source.getControllerId(), source, game)) { + cards.add(possibleCard); + } + } + if (cards.isEmpty()) { + return true; + } + TargetCard target = new TargetCard(0, 1, Zone.ALL, filter); + target.withNotTarget(true); + controller.choose(outcome, cards, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + Effect returnToBattlefieldAttachedEffect = new ReturnToBattlefieldAttachedEffect(); + returnToBattlefieldAttachedEffect.setTargetPointer(new FixedTarget(card, game)); + game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnToBattlefieldAttachedEffect), source); } - - Effect returnToBattlefieldAttachedEffect = new ReturnToBattlefieldAttachedEffect(); - returnToBattlefieldAttachedEffect.setTargetPointer(hackTargetEffect.getTargetPointer()); - game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnToBattlefieldAttachedEffect), source); return true; } diff --git a/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java b/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java index 69685415036..fd252de369f 100644 --- a/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java +++ b/Mage.Sets/src/mage/cards/n/NissaOfShadowedBoughs.java @@ -3,25 +3,23 @@ package mage.cards.n; import mage.abilities.Ability; import mage.abilities.LoyaltyAbility; import mage.abilities.common.LandfallAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.PutCardFromOneOfTwoZonesOntoBattlefieldEffect; import mage.abilities.effects.common.UntapTargetEffect; import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; -import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.MenaceAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; +import mage.cards.*; import mage.constants.*; import mage.counters.CounterType; -import mage.filter.FilterCard; +import mage.counters.Counters; import mage.filter.StaticFilters; -import mage.filter.predicate.card.CardManaCostLessThanControlledLandCountPredicate; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.card.ManaValueLessThanControlledLandCountPredicate; import mage.game.Game; import mage.game.permanent.token.custom.CreatureToken; import mage.players.Player; +import mage.target.TargetCard; import mage.target.TargetPermanent; import java.util.UUID; @@ -31,11 +29,6 @@ import java.util.UUID; */ public final class NissaOfShadowedBoughs extends CardImpl { - private static final FilterCard filter = new FilterCard("card with mana value less than or equal to the number of lands you control"); - static { - filter.add(CardManaCostLessThanControlledLandCountPredicate.getInstance()); - } - public NissaOfShadowedBoughs(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{B}{G}"); @@ -53,12 +46,7 @@ public final class NissaOfShadowedBoughs extends CardImpl { this.addAbility(ability); // −5: You may put a creature card with mana value less than or equal to the number of lands you control onto the battlefield from your hand or graveyard with two +1/+1 counters on it. - Effect putCardEffect = new PutCardFromOneOfTwoZonesOntoBattlefieldEffect(filter, false, new AddCountersTargetEffect(CounterType.P1P1.createInstance(2))); - putCardEffect.setText("You may put a creature card with mana value less than or equal to " + - "the number of lands you control onto the battlefield from your hand or graveyard " + - "with two +1/+1 counters on it."); - this.addAbility(new LoyaltyAbility(putCardEffect,-5) - ); + this.addAbility(new LoyaltyAbility(new NissaOfShadowedBoughsPutCardEffect(),-5)); } private NissaOfShadowedBoughs(final NissaOfShadowedBoughs card) { @@ -104,3 +92,54 @@ class NissaOfShadowedBoughsLandEffect extends OneShotEffect { return true; } } + + +class NissaOfShadowedBoughsPutCardEffect extends OneShotEffect { + + private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with mana value less than or equal to the number of lands you control"); + static { + filter.add(ManaValueLessThanControlledLandCountPredicate.instance); + } + + NissaOfShadowedBoughsPutCardEffect() { + super(Outcome.PutCardInPlay); + this.staticText = "you may put a creature card with mana value less than or equal to " + + "the number of lands you control onto the battlefield from your hand or graveyard " + + "with two +1/+1 counters on it"; + } + + private NissaOfShadowedBoughsPutCardEffect(final NissaOfShadowedBoughsPutCardEffect effect) { + super(effect); + } + + @Override + public NissaOfShadowedBoughsPutCardEffect copy() { + return new NissaOfShadowedBoughsPutCardEffect(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.addAllCards(controller.getHand().getCards(filter, source.getControllerId(), source, game)); + cards.addAllCards(controller.getGraveyard().getCards(filter, source.getControllerId(), source, game)); + if (cards.isEmpty()) { + return true; + } + TargetCard target = new TargetCard(0, 1, Zone.ALL, filter); + target.withNotTarget(true); + controller.choose(outcome, cards, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + Counters counters = new Counters(); + counters.addCounter(CounterType.P1P1.createInstance(2)); + game.setEnterWithCounters(card.getId(), counters); + controller.moveCards(card, Zone.BATTLEFIELD, source, game); + } + return true; + } + +} diff --git a/Mage.Sets/src/mage/cards/r/RiveteersConfluence.java b/Mage.Sets/src/mage/cards/r/RiveteersConfluence.java index 89569821a5a..a275504214a 100644 --- a/Mage.Sets/src/mage/cards/r/RiveteersConfluence.java +++ b/Mage.Sets/src/mage/cards/r/RiveteersConfluence.java @@ -1,10 +1,7 @@ package mage.cards.r; import mage.abilities.Mode; -import mage.abilities.effects.common.DamageAllEffect; -import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.LoseLifeSourceControllerEffect; -import mage.abilities.effects.common.PutCardFromOneOfTwoZonesOntoBattlefieldEffect; +import mage.abilities.effects.common.*; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -42,7 +39,7 @@ public class RiveteersConfluence extends CardImpl { this.getSpellAbility().addMode(new Mode(new DamageAllEffect(1, damageFilter))); //• You may put a land card from your hand or graveyard onto the battlefield tapped. - this.getSpellAbility().addMode(new Mode(new PutCardFromOneOfTwoZonesOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A, true))); + this.getSpellAbility().addMode(new Mode(new PutCardFromHandOrGraveyardOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A, true))); } private RiveteersConfluence(final RiveteersConfluence card) { @@ -53,4 +50,4 @@ public class RiveteersConfluence extends CardImpl { public RiveteersConfluence copy() { return new RiveteersConfluence(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/s/SwiftWarkite.java b/Mage.Sets/src/mage/cards/s/SwiftWarkite.java index e82c583b550..0f7192f7cb3 100644 --- a/Mage.Sets/src/mage/cards/s/SwiftWarkite.java +++ b/Mage.Sets/src/mage/cards/s/SwiftWarkite.java @@ -1,6 +1,5 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.DelayedTriggeredAbility; @@ -9,24 +8,21 @@ import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbil import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.PutCardFromOneOfTwoZonesOntoBattlefieldEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.HasteAbility; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.ComparisonType; -import mage.constants.Duration; -import mage.constants.Outcome; +import mage.cards.*; +import mage.constants.*; import mage.filter.FilterCard; 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.targetpointer.FixedTarget; +import java.util.UUID; + /** * * @author jeffwadsworth @@ -50,10 +46,7 @@ public final class SwiftWarkite extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Swift Warkite enters the battlefield, you may put a creature card with converted mana cost 3 or less from your hand or graveyard onto the battlefield. That creature gains haste. Return it to your hand at the beginning of the next end step. - this.addAbility(new EntersBattlefieldTriggeredAbility( - new PutCardFromOneOfTwoZonesOntoBattlefieldEffect(filter, false, new SwiftWarkiteEffect()), - true) - ); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SwiftWarkiteEffect())); } private SwiftWarkite(final SwiftWarkite card) { @@ -68,9 +61,17 @@ public final class SwiftWarkite extends CardImpl { class SwiftWarkiteEffect extends OneShotEffect { + private static final FilterCard filter = new FilterCard("creature card with mana value 3 or less"); + + static { + filter.add(CardType.CREATURE.getPredicate()); + filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4)); + } + SwiftWarkiteEffect() { - super(Outcome.AddAbility); - this.staticText = "That creature gains haste. Return it to your hand at the beginning of the next end step"; + super(Outcome.PutCardInPlay); + this.staticText = "you may put a creature card with mana value 3 or less from your hand or graveyard onto the battlefield. " + + "That creature gains haste. Return it to your hand at the beginning of the next end step"; } private SwiftWarkiteEffect(final SwiftWarkiteEffect effect) { @@ -84,19 +85,33 @@ class SwiftWarkiteEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent movedCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); - if (movedCreature == null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { return false; } + Cards cards = new CardsImpl(); + cards.addAllCards(controller.getHand().getCards(filter, source.getControllerId(), source, game)); + cards.addAllCards(controller.getGraveyard().getCards(filter, source.getControllerId(), source, game)); + if (cards.isEmpty()) { + return true; + } + TargetCard target = new TargetCard(0, 1, Zone.ALL, filter); + target.withNotTarget(true); + controller.choose(outcome, cards, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + controller.moveCards(card, Zone.BATTLEFIELD, source, game); - ContinuousEffect gainHasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); - gainHasteEffect.setTargetPointer(new FixedTarget(movedCreature.getId(), movedCreature.getZoneChangeCounter(game))); - game.addEffect(gainHasteEffect, source); + ContinuousEffect gainHasteEffect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom); + gainHasteEffect.setTargetPointer(new FixedTarget(card, game)); + game.addEffect(gainHasteEffect, source); - Effect returnToHandEffect = new ReturnToHandTargetEffect(); - returnToHandEffect.setTargetPointer(new FixedTarget(movedCreature.getId(), movedCreature.getZoneChangeCounter(game))); - DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnToHandEffect); - game.addDelayedTriggeredAbility(delayedAbility, source); + Effect returnToHandEffect = new ReturnToHandTargetEffect(); + returnToHandEffect.setTargetPointer(new FixedTarget(card, game)); + DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(returnToHandEffect); + game.addDelayedTriggeredAbility(delayedAbility, source); + } return true; } + } diff --git a/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java b/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java index 70861dd7dd4..3f6f1e4ba4b 100644 --- a/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java +++ b/Mage.Sets/src/mage/cards/v/Vault101BirthdayParty.java @@ -91,8 +91,11 @@ class Vault101BirthdayPartyEffect extends OneShotEffect { return false; } Cards cards = new CardsImpl(); - cards.addAllCards(player.getHand().getCards(filter, game)); - cards.addAllCards(player.getGraveyard().getCards(filter, game)); + cards.addAllCards(player.getHand().getCards(filter, source.getControllerId(), source, game)); + cards.addAllCards(player.getGraveyard().getCards(filter, source.getControllerId(), source, game)); + if (cards.isEmpty()) { + return false; + } TargetCard targetCard = new TargetCard(0, 1, Zone.ALL, filter); targetCard.withNotTarget(true); player.choose(outcome, cards, targetCard, source, game); @@ -103,7 +106,7 @@ class Vault101BirthdayPartyEffect extends OneShotEffect { player.moveCards(card, Zone.BATTLEFIELD, source, game); Permanent equipment = game.getPermanent(card.getId()); if (equipment == null || !equipment.hasSubtype(SubType.EQUIPMENT, game)) { - return false; + return true; } TargetPermanent targetPermanent = new TargetControlledCreaturePermanent(0, 1); targetCard.withNotTarget(true); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/PutCardFromOneOfTwoZonesOntoBattlefieldEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/PutCardFromOneOfTwoZonesOntoBattlefieldEffectTest.java index 95e717cbf5d..51f1f575622 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/PutCardFromOneOfTwoZonesOntoBattlefieldEffectTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/PutCardFromOneOfTwoZonesOntoBattlefieldEffectTest.java @@ -93,7 +93,6 @@ public class PutCardFromOneOfTwoZonesOntoBattlefieldEffectTest extends CardTestP setStrictChooseMode(true); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "-2"); - setChoice(playerA, "Yes"); // For player this choice is "Hand" but tests require "Yes" setChoice(playerA, vorpal); setStopAt(1, PhaseStep.END_TURN); @@ -113,7 +112,7 @@ public class PutCardFromOneOfTwoZonesOntoBattlefieldEffectTest extends CardTestP public void testNissaCanPlay() { addCard(Zone.BATTLEFIELD, playerA, nissa); addCard(Zone.HAND, playerA, swift); // {4}{B}{R} - addCard(Zone.HAND, playerA, "Mountain", 5); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); addCard(Zone.HAND, playerA, "Mountain"); setStrictChooseMode(true); @@ -121,7 +120,6 @@ public class PutCardFromOneOfTwoZonesOntoBattlefieldEffectTest extends CardTestP playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain"); activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "-5"); setChoice(playerA, swift); - setChoice(playerA, "Yes"); // Say yes to Swift Warkite's ETB (no further choice needed since there are no possible options setStopAt(1, PhaseStep.END_TURN); @@ -145,7 +143,6 @@ public class PutCardFromOneOfTwoZonesOntoBattlefieldEffectTest extends CardTestP addCard(Zone.HAND, playerA, swift); // {4}{B}{R} addCard(Zone.HAND, playerA, "Mountain"); - setStrictChooseMode(true); playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mountain"); @@ -174,7 +171,6 @@ public class PutCardFromOneOfTwoZonesOntoBattlefieldEffectTest extends CardTestP setStrictChooseMode(true); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, swift); - setChoice(playerA, "Yes"); // Yes to activating Swift Warkite's ETB setChoice(playerA, sliver); // Pick the sliver for the ETB setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/DanithaBenaliasHopeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/DanithaBenaliasHopeTest.java index f6b7e534670..c74f11078cc 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/DanithaBenaliasHopeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/dmu/DanithaBenaliasHopeTest.java @@ -5,7 +5,6 @@ import mage.abilities.keyword.FlyingAbility; import mage.constants.PhaseStep; import mage.constants.Zone; import org.junit.Test; -import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestPlayerBase; /** @@ -45,7 +44,7 @@ public class DanithaBenaliasHopeTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, danitha); setChoice(playerA, true); // attempt to use ability - setChoice(playerA, TestPlayer.CHOICE_SKIP); + // choice skip not needed setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); @@ -105,7 +104,7 @@ public class DanithaBenaliasHopeTest extends CardTestPlayerBase { setChoice(playerA, true); // use ability waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, swords, danitha); - setChoice(playerA, TestPlayer.CHOICE_SKIP); // no longer can attach Aura + // no longer can attach Aura setStrictChooseMode(true); setStopAt(1, PhaseStep.END_TURN); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/woc/LiberatedLivestockTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/woc/LiberatedLivestockTest.java index bbc9f18bfbb..bf87a513dd7 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/woc/LiberatedLivestockTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/woc/LiberatedLivestockTest.java @@ -199,7 +199,7 @@ public class LiberatedLivestockTest extends CardTestPlayerBase { castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, MURDER, LIBERATEDLIVESTOCK); addTarget(playerA, KEENSENSE); addTarget(playerA, ARACHNOFORM); - addTarget(playerA, TestPlayer.TARGET_SKIP); + // no possible targets thus no need to target skip waitStackResolved(1, PhaseStep.END_TURN); execute(); diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutCardFromHandOrGraveyardOntoBattlefieldEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutCardFromHandOrGraveyardOntoBattlefieldEffect.java new file mode 100644 index 00000000000..aae937ef8d2 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/PutCardFromHandOrGraveyardOntoBattlefieldEffect.java @@ -0,0 +1,69 @@ +package mage.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetCard; +import mage.util.CardUtil; + +/** + * @author xenohedron + */ +public class PutCardFromHandOrGraveyardOntoBattlefieldEffect extends OneShotEffect { + + private final FilterCard filter; + private final boolean tapped; + + /** + * @param filter for selecting a card (don't include zone-related text in filter message) + * @param tapped ETB tapped if true + */ + public PutCardFromHandOrGraveyardOntoBattlefieldEffect(FilterCard filter, boolean tapped) { + super(Outcome.PutCardInPlay); + this.filter = filter; + this.tapped = tapped; + this.staticText = "you may put " + CardUtil.addArticle(filter.getMessage()) + + " from your hand or graveyard onto the battlefield" + (tapped ? " tapped" : ""); + } + + private PutCardFromHandOrGraveyardOntoBattlefieldEffect(final PutCardFromHandOrGraveyardOntoBattlefieldEffect effect) { + super(effect); + this.filter = effect.filter; + this.tapped = effect.tapped; + } + + @Override + public PutCardFromHandOrGraveyardOntoBattlefieldEffect copy() { + return new PutCardFromHandOrGraveyardOntoBattlefieldEffect(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.addAllCards(controller.getHand().getCards(filter, source.getControllerId(), source, game)); + cards.addAllCards(controller.getGraveyard().getCards(filter, source.getControllerId(), source, game)); + if (cards.isEmpty()) { + return true; + } + TargetCard target = new TargetCard(0, 1, Zone.ALL, filter); + target.withNotTarget(true); + controller.choose(outcome, cards, target, source, game); + Card card = game.getCard(target.getFirstTarget()); + if (card != null) { + controller.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, false, null); + } + return true; + } + +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/PutCardFromOneOfTwoZonesOntoBattlefieldEffect.java b/Mage/src/main/java/mage/abilities/effects/common/PutCardFromOneOfTwoZonesOntoBattlefieldEffect.java deleted file mode 100644 index 3376fa56b99..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/PutCardFromOneOfTwoZonesOntoBattlefieldEffect.java +++ /dev/null @@ -1,164 +0,0 @@ -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.Mode; -import mage.abilities.effects.Effect; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.Cards; -import mage.cards.CardsImpl; -import mage.constants.CommanderCardType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.filter.FilterCard; -import mage.filter.StaticFilters; -import mage.filter.common.FilterCreatureCard; -import mage.game.Game; -import mage.players.Player; -import mage.target.TargetCard; -import mage.target.common.TargetCardInCommandZone; -import mage.target.common.TargetCardInGraveyard; -import mage.target.common.TargetCardInHand; -import mage.target.targetpointer.FixedTarget; -import mage.util.CardUtil; - -/** - * "Put a {filter} card from {zone 1} or {zone 2} onto the battlefield. - * - * @author TheElk801, Alex-Vasile - */ -public class PutCardFromOneOfTwoZonesOntoBattlefieldEffect extends OneShotEffect { - - private final FilterCard filterCard; - private final boolean tapped; - private final Effect effectToApplyOnPermanent; - private final Zone zone1; - private final Zone zone2; - - public PutCardFromOneOfTwoZonesOntoBattlefieldEffect(FilterCard filterCard) { - this(filterCard, false); - } - - public PutCardFromOneOfTwoZonesOntoBattlefieldEffect(FilterCard filterCard, boolean tapped) { - this(filterCard, tapped, null); - } - - /** - * - * @param filterCard Filter used to filter which cards are valid choices. (no default) - * @param tapped If the permanent should enter the battlefield tapped (default is False) - * @param effectToApplyOnPermanent An effect to apply to the permanent after it enters (default null) - * See "Swift Warkite" or "Nissa of Shadowed Boughs". - * @param zone1 The first zone to pick from (default of HAND) - * @param zone2 The second zone to pick from (defualt of GRAVEYARD) - */ - public PutCardFromOneOfTwoZonesOntoBattlefieldEffect(FilterCard filterCard, boolean tapped, Effect effectToApplyOnPermanent, Zone zone1, Zone zone2) { - super(filterCard instanceof FilterCreatureCard ? Outcome.PutCreatureInPlay : Outcome.PutCardInPlay); - this.filterCard = filterCard; - this.tapped = tapped; - this.effectToApplyOnPermanent = effectToApplyOnPermanent; - this.zone1 = zone1; - this.zone2 = zone2; - } - - public PutCardFromOneOfTwoZonesOntoBattlefieldEffect(FilterCard filterCard, boolean tapped, Effect effectToApplyOnPermanent) { - this(filterCard, tapped, effectToApplyOnPermanent, Zone.HAND, Zone.GRAVEYARD); - } - - public PutCardFromOneOfTwoZonesOntoBattlefieldEffect(FilterCard filterCard, Zone zone1, Zone zone2) { - this(filterCard, false, null, zone1, zone2); - } - - private PutCardFromOneOfTwoZonesOntoBattlefieldEffect(final PutCardFromOneOfTwoZonesOntoBattlefieldEffect effect) { - super(effect); - this.filterCard = effect.filterCard; - this.tapped = effect.tapped; - this.zone1 = effect.zone1; - this.zone2 = effect.zone2; - if (effect.effectToApplyOnPermanent != null) { - this.effectToApplyOnPermanent = effect.effectToApplyOnPermanent.copy(); - } else { - this.effectToApplyOnPermanent = null; - } - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - - Cards cardsInZone1 = getCardsFromZone(game, controller, zone1); - Cards cardsInZone2 = getCardsFromZone(game, controller, zone2); - - boolean cardsAvailableInZone1 = cardsInZone1.count(filterCard, game) > 0; - boolean cardsAvailableInZone2 = cardsInZone2.count(filterCard, game) > 0; - if (!cardsAvailableInZone1 && !cardsAvailableInZone2) { - return false; - } - - boolean choose1stZone; - if (cardsAvailableInZone1 && cardsAvailableInZone2) { - choose1stZone = controller.chooseUse(outcome, "Where do you want to chose the card from?", - null, zone1.name(), zone2.name(), source, game); - } else { - choose1stZone = cardsAvailableInZone1; - } - - Zone zone = choose1stZone ? zone1 : zone2; - Cards cards = choose1stZone ? cardsInZone1 : cardsInZone2; - TargetCard targetCard; - - switch (zone) { - case HAND: - targetCard = new TargetCardInHand(filterCard); - break; - case GRAVEYARD: - targetCard = new TargetCardInGraveyard(filterCard); - break; - case COMMAND: - targetCard = new TargetCardInCommandZone(filterCard); - break; - default: - return false; - } - controller.choose(outcome, cards, targetCard, source, game); - Card card = game.getCard(targetCard.getFirstTarget()); - if (card == null || !controller.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, false, null)) { - return false; - } - - if (effectToApplyOnPermanent != null) { - effectToApplyOnPermanent.setTargetPointer(new FixedTarget(card.getId())); - effectToApplyOnPermanent.apply(game, source); - } - return true; - } - - private static Cards getCardsFromZone(Game game, Player player, Zone zone) { - switch (zone) { - case HAND: - return player.getHand(); - case COMMAND: - return new CardsImpl(game.getCommanderCardsFromCommandZone(player, CommanderCardType.ANY)); - case GRAVEYARD: - return player.getGraveyard(); - default: - return new CardsImpl(); - } - } - - @Override - public PutCardFromOneOfTwoZonesOntoBattlefieldEffect copy() { - return new PutCardFromOneOfTwoZonesOntoBattlefieldEffect(this); - } - - @Override - public String getText(Mode mode) { - return "you may put " + CardUtil.addArticle(this.filterCard.getMessage()) + - " from your hand or graveyard onto the battlefield" + - (this.tapped ? " tapped" : "") + - (effectToApplyOnPermanent == null ? "" : ". " + effectToApplyOnPermanent.getText(mode)); - } -} diff --git a/Mage/src/main/java/mage/filter/predicate/card/CardManaCostLessThanControlledLandCountPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/CardManaCostLessThanControlledLandCountPredicate.java deleted file mode 100644 index 4bf53171825..00000000000 --- a/Mage/src/main/java/mage/filter/predicate/card/CardManaCostLessThanControlledLandCountPredicate.java +++ /dev/null @@ -1,31 +0,0 @@ -package mage.filter.predicate.card; - -import mage.cards.Card; -import mage.filter.StaticFilters; -import mage.filter.predicate.Predicate; -import mage.game.Game; - -/** - * @author Plopman, Alex-Vasile - */ -public class CardManaCostLessThanControlledLandCountPredicate implements Predicate { - - private static final String string = "card with mana value less than or equal to the number of lands you control"; - private static final CardManaCostLessThanControlledLandCountPredicate instance = new CardManaCostLessThanControlledLandCountPredicate(); - - private CardManaCostLessThanControlledLandCountPredicate() { } - - public static CardManaCostLessThanControlledLandCountPredicate getInstance() { - return instance; - } - - @Override - public boolean apply(Card input, Game game) { - return input.getManaValue() <= game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_CONTROLLED_PERMANENT_LAND, input.getOwnerId(), game).size(); - } - - @Override - public String toString() { - return string; - } -} diff --git a/Mage/src/main/java/mage/filter/predicate/card/ManaValueLessThanControlledLandCountPredicate.java b/Mage/src/main/java/mage/filter/predicate/card/ManaValueLessThanControlledLandCountPredicate.java new file mode 100644 index 00000000000..4d0da691a9a --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/card/ManaValueLessThanControlledLandCountPredicate.java @@ -0,0 +1,24 @@ +package mage.filter.predicate.card; + +import mage.MageObject; +import mage.filter.StaticFilters; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; +import mage.game.Game; + +/** + * @author xenohedron + */ +public enum ManaValueLessThanControlledLandCountPredicate implements ObjectSourcePlayerPredicate { + instance; + + @Override + public boolean apply(ObjectSourcePlayer input, Game game) { + return input.getObject().getManaValue() <= game.getBattlefield().countAll(StaticFilters.FILTER_LAND, game.getControllerId(input.getSourceId()), game); + } + + @Override + public String toString() { + return "mana value less than or equal to the number of lands you control"; + } +}