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;
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)));

View file

@ -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<ObjectPlayer<Controllable>> {
enum KadenaSlinkingSorcererPredicate implements Predicate<Card> {
instance;
@Override
public boolean apply(ObjectPlayer<Controllable> 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;
}

View file

@ -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) {

View file

@ -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);
}
}

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);
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
}