From 97c427375de6c40ea373eee0f219af189e821354 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 24 Jul 2020 21:30:03 +0400 Subject: [PATCH] Fixed face down cost modification (related to 653a2dd7b2edc876fbd6cc5506add60f1e65d84a) --- Mage.Sets/src/mage/cards/d/DreamChisel.java | 11 +++--- .../mage/cards/k/KadenaSlinkingSorcerer.java | 31 ++++++++--------- .../src/mage/cards/o/ObscuringAether.java | 9 +++-- .../cards/abilities/keywords/MorphTest.java | 34 ++++++++++++++++++- .../other/FaceDownCastablePredicate.java | 24 +++++++++++++ .../main/java/mage/players/PlayerImpl.java | 12 ++++++- 6 files changed, 92 insertions(+), 29 deletions(-) create mode 100644 Mage/src/main/java/mage/filter/predicate/other/FaceDownCastablePredicate.java diff --git a/Mage.Sets/src/mage/cards/d/DreamChisel.java b/Mage.Sets/src/mage/cards/d/DreamChisel.java index 8508e9b24b..22503b4d2a 100644 --- a/Mage.Sets/src/mage/cards/d/DreamChisel.java +++ b/Mage.Sets/src/mage/cards/d/DreamChisel.java @@ -1,7 +1,5 @@ - package mage.cards.d; -import java.util.UUID; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.cards.CardImpl; @@ -9,10 +7,11 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.other.FaceDownCastablePredicate; + +import java.util.UUID; /** - * * @author North */ public final class DreamChisel extends CardImpl { @@ -20,11 +19,11 @@ public final class DreamChisel extends CardImpl { private static final FilterCreatureCard filter = new FilterCreatureCard("Face-down creature spells"); static { - filter.add(FaceDownPredicate.instance); + filter.add(FaceDownCastablePredicate.instance); } public DreamChisel(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}"); // Face-down creature spells you cast cost {1} less to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); diff --git a/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java b/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java index 1b8349cf81..181c718676 100644 --- a/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java +++ b/Mage.Sets/src/mage/cards/k/KadenaSlinkingSorcerer.java @@ -5,6 +5,7 @@ import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; @@ -14,10 +15,9 @@ import mage.constants.WatcherScope; import mage.filter.FilterCard; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.ObjectPlayer; -import mage.filter.predicate.ObjectPlayerPredicate; +import mage.filter.predicate.Predicate; +import mage.filter.predicate.other.FaceDownCastablePredicate; import mage.filter.predicate.other.FaceDownPredicate; -import mage.game.Controllable; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; @@ -32,12 +32,12 @@ import java.util.UUID; */ public final class KadenaSlinkingSorcerer extends CardImpl { - private static final FilterCard filter = new FilterCard(); - private static final FilterPermanent filter2 = new FilterControlledCreaturePermanent("a face-down creature"); + private static final FilterCard filterFirstFaceDownSpell = new FilterCard("first face-down creature spell"); + private static final FilterPermanent filterFaceDownPermanent = new FilterControlledCreaturePermanent("a face-down creature"); static { - filter.add(KadenaSlinkingSorcererPredicate.instance); - filter2.add(FaceDownPredicate.instance); + filterFirstFaceDownSpell.add(KadenaSlinkingSorcererPredicate.instance); + filterFaceDownPermanent.add(FaceDownPredicate.instance); } public KadenaSlinkingSorcerer(UUID ownerId, CardSetInfo setInfo) { @@ -51,13 +51,13 @@ public final class KadenaSlinkingSorcerer extends CardImpl { // The first face-down creature spell you cast each turn costs {3} less to cast. this.addAbility(new SimpleStaticAbility( - new SpellsCostReductionControllerEffect(filter, 3) + new SpellsCostReductionControllerEffect(filterFirstFaceDownSpell, 3) .setText("The first face-down creature spell you cast each turn costs {3} less to cast.") ), new KadenaSlinkingSorcererWatcher()); // Whenever a face-down creature enters the battlefield under your control, draw a card. this.addAbility(new EntersBattlefieldControlledTriggeredAbility( - new DrawCardSourceControllerEffect(1), filter2 + new DrawCardSourceControllerEffect(1), filterFaceDownPermanent )); } @@ -71,16 +71,15 @@ public final class KadenaSlinkingSorcerer extends CardImpl { } } -enum KadenaSlinkingSorcererPredicate implements ObjectPlayerPredicate> { +enum KadenaSlinkingSorcererPredicate implements Predicate { instance; @Override - public boolean apply(ObjectPlayer input, Game game) { - if (input.getObject() instanceof Spell - && ((Spell) input.getObject()).isCreature() - && ((Spell) input.getObject()).isFaceDown(game)) { - KadenaSlinkingSorcererWatcher watcher = game.getState().getWatcher(KadenaSlinkingSorcererWatcher.class); - return watcher != null && !watcher.castFaceDownThisTurn(input.getPlayerId()); + public boolean apply(Card input, Game game) { + KadenaSlinkingSorcererWatcher watcher = game.getState().getWatcher(KadenaSlinkingSorcererWatcher.class); + if (watcher != null) { + return FaceDownCastablePredicate.instance.apply(input, game) + && !watcher.castFaceDownThisTurn(input.getOwnerId()); } return false; } diff --git a/Mage.Sets/src/mage/cards/o/ObscuringAether.java b/Mage.Sets/src/mage/cards/o/ObscuringAether.java index 1b4e559253..6ede93fc40 100644 --- a/Mage.Sets/src/mage/cards/o/ObscuringAether.java +++ b/Mage.Sets/src/mage/cards/o/ObscuringAether.java @@ -1,7 +1,5 @@ - package mage.cards.o; -import java.util.UUID; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -14,10 +12,11 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.other.FaceDownPredicate; +import mage.filter.predicate.other.FaceDownCastablePredicate; + +import java.util.UUID; /** - * * @author LevelX2 */ public final class ObscuringAether extends CardImpl { @@ -25,7 +24,7 @@ public final class ObscuringAether extends CardImpl { private static final FilterCreatureCard filter = new FilterCreatureCard("Face-down creature spells"); static { - filter.add(FaceDownPredicate.instance); + filter.add(FaceDownCastablePredicate.instance); } public ObscuringAether(UUID ownerId, CardSetInfo setInfo) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java index 9337b72f3b..9b4a52df42 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MorphTest.java @@ -1089,7 +1089,7 @@ public class MorphTest extends CardTestPlayerBase { } @Test - public void test_MorphWithCostReductionMustBePlayable_MorphCondition() { + public void test_MorphWithCostReductionMustBePlayable_MorphCondition1() { // {1}{U} creature // Morph {1}{U} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.) // When Willbender is turned face up, change the target of target spell or ability with a single target. @@ -1110,4 +1110,36 @@ public class MorphTest extends CardTestPlayerBase { assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); } + + @Test + public void test_MorphWithCostReductionMustBePlayable_MorphCondition2() { + // {1}{U} creature + // Morph {1}{U} (You may cast this card face down as a 2/2 creature for {3}. Turn it face up any time for its morph cost.) + // When Willbender is turned face up, change the target of target spell or ability with a single target. + addCard(Zone.HAND, playerA, "Willbender", 2); + //addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // + // The first face-down creature spell you cast each turn costs {3} less to cast. + addCard(Zone.BATTLEFIELD, playerA, "Kadena, Slinking Sorcerer"); + + // creature one - get cost reduce + checkPlayableAbility("can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", true); + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender"); + setChoice(playerA, "Yes"); // morph + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + // creature two - do not get cost reduce + checkPlayableAbility("can't by no reduce", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", false); + + // on next turn it can cost reduce again + checkPlayableAbility("can't by not your turn", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", false); + checkPlayableAbility("can", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Willbender", true); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + } } diff --git a/Mage/src/main/java/mage/filter/predicate/other/FaceDownCastablePredicate.java b/Mage/src/main/java/mage/filter/predicate/other/FaceDownCastablePredicate.java new file mode 100644 index 0000000000..e7d3d55f5b --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/other/FaceDownCastablePredicate.java @@ -0,0 +1,24 @@ +package mage.filter.predicate.other; + +import mage.abilities.keyword.MorphAbility; +import mage.cards.Card; +import mage.filter.predicate.Predicate; +import mage.game.Game; + +/** + * @author JayDi85 + */ +public enum FaceDownCastablePredicate implements Predicate { + instance; + + @Override + public boolean apply(Card input, Game game) { + // is card able to cast as face down + return input.getAbilities(game).containsClass(MorphAbility.class); + } + + @Override + public String toString() { + return "Face-down"; + } +} diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index f5bfff2e08..4db2e874db 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -3156,6 +3156,11 @@ public abstract class PlayerImpl implements Player, Serializable { sourceObject.adjustCosts(copyAbility, game); game.getContinuousEffects().costModification(copyAbility, game); + // reduced all cost + if (copyAbility.getManaCostsToPay().isEmpty()) { + return true; + } + for (Mana mana : copyAbility.getManaCostsToPay().getOptions()) { if (availableMana.enough(mana)) { return true; @@ -3193,6 +3198,11 @@ public abstract class PlayerImpl implements Player, Serializable { sourceObject.adjustCosts(copyAbility, game); game.getContinuousEffects().costModification(copyAbility, game); + // reduced all cost + if (copyAbility.getManaCostsToPay().isEmpty()) { + return true; + } + for (Mana mana : copyAbility.getManaCostsToPay().getOptions()) { if (availableMana.enough(mana)) { return true; @@ -3260,7 +3270,7 @@ public abstract class PlayerImpl implements Player, Serializable { // So make it available all the time boolean canUse; if (ability instanceof MorphAbility && object instanceof Card && game.canPlaySorcery(getId())) { - canUse = canPlayCardByAlternateCost((Card) object, availableMana, ability, game); + canUse = canPlayCardByAlternateCost((Card) object, availableMana, playAbility, game); } else { canUse = canPlay(playAbility, availableMana, object, game); // canPlay already checks alternative source costs and all conditions }