Fixed face down cost modification (related to 653a2dd7b2)

This commit is contained in:
Oleg Agafonov 2020-07-24 21:30:03 +04:00
parent dfaf09e76c
commit 97c427375d
6 changed files with 92 additions and 29 deletions

View file

@ -1,7 +1,5 @@
package mage.cards.d; package mage.cards.d;
import java.util.UUID;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -9,10 +7,11 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.other.FaceDownPredicate; import mage.filter.predicate.other.FaceDownCastablePredicate;
import java.util.UUID;
/** /**
*
* @author North * @author North
*/ */
public final class DreamChisel extends CardImpl { 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"); private static final FilterCreatureCard filter = new FilterCreatureCard("Face-down creature spells");
static { static {
filter.add(FaceDownPredicate.instance); filter.add(FaceDownCastablePredicate.instance);
} }
public DreamChisel(UUID ownerId, CardSetInfo setInfo) { 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. // Face-down creature spells you cast cost {1} less to cast.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 1)));

View file

@ -5,6 +5,7 @@ import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect; import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -14,10 +15,9 @@ import mage.constants.WatcherScope;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.FilterPermanent; import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.ObjectPlayer; import mage.filter.predicate.Predicate;
import mage.filter.predicate.ObjectPlayerPredicate; import mage.filter.predicate.other.FaceDownCastablePredicate;
import mage.filter.predicate.other.FaceDownPredicate; import mage.filter.predicate.other.FaceDownPredicate;
import mage.game.Controllable;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.stack.Spell; import mage.game.stack.Spell;
@ -32,12 +32,12 @@ import java.util.UUID;
*/ */
public final class KadenaSlinkingSorcerer extends CardImpl { public final class KadenaSlinkingSorcerer extends CardImpl {
private static final FilterCard filter = new FilterCard(); private static final FilterCard filterFirstFaceDownSpell = new FilterCard("first face-down creature spell");
private static final FilterPermanent filter2 = new FilterControlledCreaturePermanent("a face-down creature"); private static final FilterPermanent filterFaceDownPermanent = new FilterControlledCreaturePermanent("a face-down creature");
static { static {
filter.add(KadenaSlinkingSorcererPredicate.instance); filterFirstFaceDownSpell.add(KadenaSlinkingSorcererPredicate.instance);
filter2.add(FaceDownPredicate.instance); filterFaceDownPermanent.add(FaceDownPredicate.instance);
} }
public KadenaSlinkingSorcerer(UUID ownerId, CardSetInfo setInfo) { 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. // The first face-down creature spell you cast each turn costs {3} less to cast.
this.addAbility(new SimpleStaticAbility( 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.") .setText("The first face-down creature spell you cast each turn costs {3} less to cast.")
), new KadenaSlinkingSorcererWatcher()); ), new KadenaSlinkingSorcererWatcher());
// Whenever a face-down creature enters the battlefield under your control, draw a card. // Whenever a face-down creature enters the battlefield under your control, draw a card.
this.addAbility(new EntersBattlefieldControlledTriggeredAbility( 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<ObjectPlayer<Controllable>> { enum KadenaSlinkingSorcererPredicate implements Predicate<Card> {
instance; instance;
@Override @Override
public boolean apply(ObjectPlayer<Controllable> input, Game game) { public boolean apply(Card input, Game game) {
if (input.getObject() instanceof Spell KadenaSlinkingSorcererWatcher watcher = game.getState().getWatcher(KadenaSlinkingSorcererWatcher.class);
&& ((Spell) input.getObject()).isCreature() if (watcher != null) {
&& ((Spell) input.getObject()).isFaceDown(game)) { return FaceDownCastablePredicate.instance.apply(input, game)
KadenaSlinkingSorcererWatcher watcher = game.getState().getWatcher(KadenaSlinkingSorcererWatcher.class); && !watcher.castFaceDownThisTurn(input.getOwnerId());
return watcher != null && !watcher.castFaceDownThisTurn(input.getPlayerId());
} }
return false; return false;
} }

View file

@ -1,7 +1,5 @@
package mage.cards.o; package mage.cards.o;
import java.util.UUID;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
@ -14,10 +12,11 @@ import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.other.FaceDownPredicate; import mage.filter.predicate.other.FaceDownCastablePredicate;
import java.util.UUID;
/** /**
*
* @author LevelX2 * @author LevelX2
*/ */
public final class ObscuringAether extends CardImpl { 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"); private static final FilterCreatureCard filter = new FilterCreatureCard("Face-down creature spells");
static { static {
filter.add(FaceDownPredicate.instance); filter.add(FaceDownCastablePredicate.instance);
} }
public ObscuringAether(UUID ownerId, CardSetInfo setInfo) { public ObscuringAether(UUID ownerId, CardSetInfo setInfo) {

View file

@ -1089,7 +1089,7 @@ public class MorphTest extends CardTestPlayerBase {
} }
@Test @Test
public void test_MorphWithCostReductionMustBePlayable_MorphCondition() { public void test_MorphWithCostReductionMustBePlayable_MorphCondition1() {
// {1}{U} creature // {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.) // 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. // 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); 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);
}
} }

View file

@ -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<Card> {
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";
}
}

View file

@ -3156,6 +3156,11 @@ public abstract class PlayerImpl implements Player, Serializable {
sourceObject.adjustCosts(copyAbility, game); sourceObject.adjustCosts(copyAbility, game);
game.getContinuousEffects().costModification(copyAbility, game); game.getContinuousEffects().costModification(copyAbility, game);
// reduced all cost
if (copyAbility.getManaCostsToPay().isEmpty()) {
return true;
}
for (Mana mana : copyAbility.getManaCostsToPay().getOptions()) { for (Mana mana : copyAbility.getManaCostsToPay().getOptions()) {
if (availableMana.enough(mana)) { if (availableMana.enough(mana)) {
return true; return true;
@ -3193,6 +3198,11 @@ public abstract class PlayerImpl implements Player, Serializable {
sourceObject.adjustCosts(copyAbility, game); sourceObject.adjustCosts(copyAbility, game);
game.getContinuousEffects().costModification(copyAbility, game); game.getContinuousEffects().costModification(copyAbility, game);
// reduced all cost
if (copyAbility.getManaCostsToPay().isEmpty()) {
return true;
}
for (Mana mana : copyAbility.getManaCostsToPay().getOptions()) { for (Mana mana : copyAbility.getManaCostsToPay().getOptions()) {
if (availableMana.enough(mana)) { if (availableMana.enough(mana)) {
return true; return true;
@ -3260,7 +3270,7 @@ public abstract class PlayerImpl implements Player, Serializable {
// So make it available all the time // So make it available all the time
boolean canUse; boolean canUse;
if (ability instanceof MorphAbility && object instanceof Card && game.canPlaySorcery(getId())) { 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 { } else {
canUse = canPlay(playAbility, availableMana, object, game); // canPlay already checks alternative source costs and all conditions canUse = canPlay(playAbility, availableMana, object, game); // canPlay already checks alternative source costs and all conditions
} }