From d165072ac9a9c826615c3d1c1e045a90e3441bd7 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Wed, 14 Sep 2022 15:16:56 -0400 Subject: [PATCH] [DMU] Implement Defiler cycle (Ready for review) (#9460) * [DMU] Implemented Defiler of Faith * [DMU] Implemented Defiler of Dreams * [DMU] Implemented Defiler of Flesh * [DMU] Implemented Defiler of Instinct * [DMU] Implemented Defiler of Vigor * create PermanentPredicate * initial attempt at Defiler ability * some nonfunctional recommended changes * a few more requested changes * change arguments to ObjectColor from String * fix a dumb mistake * add test * a few requested changes --- .../src/mage/cards/d/DefilerOfDreams.java | 59 +++++++++ .../src/mage/cards/d/DefilerOfFaith.java | 60 +++++++++ .../src/mage/cards/d/DefilerOfFlesh.java | 68 ++++++++++ .../src/mage/cards/d/DefilerOfInstinct.java | 63 ++++++++++ .../src/mage/cards/d/DefilerOfVigor.java | 64 ++++++++++ .../src/mage/cards/i/InSearchOfGreatness.java | 2 + .../src/mage/cards/l/LithoformEngine.java | 14 +-- .../mage/cards/m/MyojinOfCrypticDreams.java | 22 ++-- Mage.Sets/src/mage/sets/DominariaUnited.java | 5 + .../cards/cost/additional/DefilersTest.java | 118 ++++++++++++++++++ .../common/MayPay2LifeForColorAbility.java | 104 +++++++++++++++ .../filter/common/FilterPermanentCard.java | 14 +-- .../mageobject/PermanentPredicate.java | 17 +++ 13 files changed, 572 insertions(+), 38 deletions(-) create mode 100644 Mage.Sets/src/mage/cards/d/DefilerOfDreams.java create mode 100644 Mage.Sets/src/mage/cards/d/DefilerOfFaith.java create mode 100644 Mage.Sets/src/mage/cards/d/DefilerOfFlesh.java create mode 100644 Mage.Sets/src/mage/cards/d/DefilerOfInstinct.java create mode 100644 Mage.Sets/src/mage/cards/d/DefilerOfVigor.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/DefilersTest.java create mode 100644 Mage/src/main/java/mage/abilities/common/MayPay2LifeForColorAbility.java create mode 100644 Mage/src/main/java/mage/filter/predicate/mageobject/PermanentPredicate.java diff --git a/Mage.Sets/src/mage/cards/d/DefilerOfDreams.java b/Mage.Sets/src/mage/cards/d/DefilerOfDreams.java new file mode 100644 index 0000000000..1b2377832a --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DefilerOfDreams.java @@ -0,0 +1,59 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.common.MayPay2LifeForColorAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.mageobject.PermanentPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DefilerOfDreams extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a blue permanent spell"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + filter.add(PermanentPredicate.instance); + } + + public DefilerOfDreams(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{U}"); + + this.subtype.add(SubType.PHYREXIAN); + this.subtype.add(SubType.SPHINX); + this.power = new MageInt(4); + this.toughness = new MageInt(3); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // As an additional cost to cast blue permanent spells, you may pay 2 life. Those spells cost {U} less to cast if you paid life this way. This effect reduces only the amount of blue mana you pay. + this.addAbility(new MayPay2LifeForColorAbility(ObjectColor.BLUE)); + + // Whenever you cast a blue permanent spell, draw a card. + this.addAbility(new SpellCastControllerTriggeredAbility( + new DrawCardSourceControllerEffect(1), filter, false + )); + } + + private DefilerOfDreams(final DefilerOfDreams card) { + super(card); + } + + @Override + public DefilerOfDreams copy() { + return new DefilerOfDreams(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DefilerOfFaith.java b/Mage.Sets/src/mage/cards/d/DefilerOfFaith.java new file mode 100644 index 0000000000..38f39229c8 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DefilerOfFaith.java @@ -0,0 +1,60 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.common.MayPay2LifeForColorAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.VigilanceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.mageobject.PermanentPredicate; +import mage.game.permanent.token.SoldierToken; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DefilerOfFaith extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a white permanent spell"); + + static { + filter.add(new ColorPredicate(ObjectColor.WHITE)); + filter.add(PermanentPredicate.instance); + } + + public DefilerOfFaith(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}"); + + this.subtype.add(SubType.PHYREXIAN); + this.subtype.add(SubType.HUMAN); + this.power = new MageInt(5); + this.toughness = new MageInt(5); + + // Vigilance + this.addAbility(VigilanceAbility.getInstance()); + + // As an additional cost to cast white permanent spells, you may pay 2 life. Those spells cost {W} less to cast if you paid life this way. This effect reduces only the amount of white mana you pay. + this.addAbility(new MayPay2LifeForColorAbility(ObjectColor.WHITE)); + + // Whenever you cast a white permanent spell, create a 1/1 white Soldier creature token. + this.addAbility(new SpellCastControllerTriggeredAbility( + new CreateTokenEffect(new SoldierToken()), filter, false + )); + } + + private DefilerOfFaith(final DefilerOfFaith card) { + super(card); + } + + @Override + public DefilerOfFaith copy() { + return new DefilerOfFaith(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DefilerOfFlesh.java b/Mage.Sets/src/mage/cards/d/DefilerOfFlesh.java new file mode 100644 index 0000000000..39022efe72 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DefilerOfFlesh.java @@ -0,0 +1,68 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.MayPay2LifeForColorAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; +import mage.abilities.keyword.MenaceAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.mageobject.PermanentPredicate; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DefilerOfFlesh extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a black permanent spell"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLACK)); + filter.add(PermanentPredicate.instance); + } + + public DefilerOfFlesh(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}"); + + this.subtype.add(SubType.PHYREXIAN); + this.subtype.add(SubType.HORROR); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // Menace + this.addAbility(new MenaceAbility(false)); + + // As an additional cost to cast black permanent spells, you may pay 2 life. Those spells cost {B} less to cast if you paid life this way. This effect reduces only the amount of black mana you pay. + this.addAbility(new MayPay2LifeForColorAbility(ObjectColor.BLACK)); + + // Whenever you cast a black permanent spell, target creature you control gets +1/+1 and gains menace until end of turn. + Ability ability = new SpellCastControllerTriggeredAbility( + new BoostTargetEffect(1, 1) + .setText("target creature you control gets +1/+1"), + filter, false + ); + ability.addEffect(new GainAbilityTargetEffect(new MenaceAbility(false)) + .setText("and gains menace until end of turn")); + ability.addTarget(new TargetControlledCreaturePermanent()); + this.addAbility(ability); + } + + private DefilerOfFlesh(final DefilerOfFlesh card) { + super(card); + } + + @Override + public DefilerOfFlesh copy() { + return new DefilerOfFlesh(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DefilerOfInstinct.java b/Mage.Sets/src/mage/cards/d/DefilerOfInstinct.java new file mode 100644 index 0000000000..412c42b0e9 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DefilerOfInstinct.java @@ -0,0 +1,63 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.MayPay2LifeForColorAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.mageobject.PermanentPredicate; +import mage.target.common.TargetAnyTarget; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DefilerOfInstinct extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a red permanent spell"); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + filter.add(PermanentPredicate.instance); + } + + public DefilerOfInstinct(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}"); + + this.subtype.add(SubType.PHYREXIAN); + this.subtype.add(SubType.KAVU); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + + // First strike + this.addAbility(FirstStrikeAbility.getInstance()); + + // As an additional cost to cast red permanent spells, you may pay 2 life. Those spells cost {R} less to cast if you paid life this way. This effect reduces only the amount of red mana you pay. + this.addAbility(new MayPay2LifeForColorAbility(ObjectColor.RED)); + + // Whenever you cast a red permanent spell, Defiler of Instinct deals 1 damage to any target. + Ability ability = new SpellCastControllerTriggeredAbility( + new DamageTargetEffect(1), filter, false + ); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(ability); + } + + private DefilerOfInstinct(final DefilerOfInstinct card) { + super(card); + } + + @Override + public DefilerOfInstinct copy() { + return new DefilerOfInstinct(this); + } +} diff --git a/Mage.Sets/src/mage/cards/d/DefilerOfVigor.java b/Mage.Sets/src/mage/cards/d/DefilerOfVigor.java new file mode 100644 index 0000000000..f24c323936 --- /dev/null +++ b/Mage.Sets/src/mage/cards/d/DefilerOfVigor.java @@ -0,0 +1,64 @@ +package mage.cards.d; + +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.common.MayPay2LifeForColorAbility; +import mage.abilities.common.SpellCastControllerTriggeredAbility; +import mage.abilities.effects.common.counter.AddCountersAllEffect; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.counters.CounterType; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.predicate.mageobject.PermanentPredicate; + +import java.util.UUID; + +/** + * @author TheElk801 + */ +public final class DefilerOfVigor extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("a green permanent spell"); + + static { + filter.add(new ColorPredicate(ObjectColor.GREEN)); + filter.add(PermanentPredicate.instance); + } + + public DefilerOfVigor(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + + this.subtype.add(SubType.PHYREXIAN); + this.subtype.add(SubType.WURM); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Trample + this.addAbility(TrampleAbility.getInstance()); + + // As an additional cost to cast green permanent spells, you may pay 2 life. Those spells cost {G} less to cast if you paid life this way. This effect reduces only the amount of green mana you pay. + this.addAbility(new MayPay2LifeForColorAbility(ObjectColor.GREEN)); + + // Whenever you cast a green permanent spell, put a +1/+1 counter on each creature you control. + this.addAbility(new SpellCastControllerTriggeredAbility( + new AddCountersAllEffect( + CounterType.P1P1.createInstance(), + StaticFilters.FILTER_CONTROLLED_CREATURE + ), filter, false + )); + } + + private DefilerOfVigor(final DefilerOfVigor card) { + super(card); + } + + @Override + public DefilerOfVigor copy() { + return new DefilerOfVigor(this); + } +} diff --git a/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java b/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java index d904b1d511..606c183981 100644 --- a/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java +++ b/Mage.Sets/src/mage/cards/i/InSearchOfGreatness.java @@ -16,6 +16,7 @@ import mage.filter.FilterCard; import mage.filter.StaticFilters; import mage.filter.common.FilterPermanentCard; import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.predicate.mageobject.PermanentPredicate; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; @@ -85,6 +86,7 @@ class InSearchOfGreatnessEffect extends OneShotEffect { .orElse(0); FilterCard filter = new FilterPermanentCard(); filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, manaValue + 1)); + filter.add(PermanentPredicate.instance); return CardUtil.castSpellWithAttributesForFree( controller, source, game, new CardsImpl(controller.getHand()), filter ) || controller.scry(1, source, game); diff --git a/Mage.Sets/src/mage/cards/l/LithoformEngine.java b/Mage.Sets/src/mage/cards/l/LithoformEngine.java index 3a259a0de6..bbebcd0cd1 100644 --- a/Mage.Sets/src/mage/cards/l/LithoformEngine.java +++ b/Mage.Sets/src/mage/cards/l/LithoformEngine.java @@ -16,11 +16,10 @@ import mage.constants.TargetController; import mage.filter.FilterSpell; import mage.filter.FilterStackObject; import mage.filter.common.FilterInstantOrSorcerySpell; -import mage.filter.predicate.Predicate; +import mage.filter.predicate.mageobject.PermanentPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.StackAbility; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.TargetSpell; import mage.target.common.TargetActivatedOrTriggeredAbility; @@ -43,7 +42,7 @@ public final class LithoformEngine extends CardImpl { filter.add(TargetController.YOU.getControllerPredicate()); filter2.add(TargetController.YOU.getControllerPredicate()); filter3.add(TargetController.YOU.getControllerPredicate()); - filter3.add(LithoformEnginePredicate.instance); + filter3.add(PermanentPredicate.instance); } public LithoformEngine(UUID ownerId, CardSetInfo setInfo) { @@ -82,15 +81,6 @@ public final class LithoformEngine extends CardImpl { } } -enum LithoformEnginePredicate implements Predicate { - instance; - - @Override - public boolean apply(StackObject input, Game game) { - return input.isPermanent(game); - } -} - class LithoformEngineEffect extends OneShotEffect { public LithoformEngineEffect() { diff --git a/Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java b/Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java index da089a8c8c..ecae134392 100644 --- a/Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java +++ b/Mage.Sets/src/mage/cards/m/MyojinOfCrypticDreams.java @@ -16,9 +16,7 @@ import mage.constants.SuperType; import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.FilterSpell; -import mage.filter.predicate.Predicate; -import mage.game.Game; -import mage.game.stack.StackObject; +import mage.filter.predicate.mageobject.PermanentPredicate; import mage.target.TargetSpell; import mage.watchers.common.CastFromHandWatcher; @@ -30,9 +28,10 @@ import java.util.UUID; public class MyojinOfCrypticDreams extends CardImpl { private static final FilterSpell permanentSpellFilter = new FilterSpell("permanent spell you control"); + static { permanentSpellFilter.add(TargetController.YOU.getControllerPredicate()); - permanentSpellFilter.add(MyojinOfCrypticDreamsPredicate.instance); + permanentSpellFilter.add(PermanentPredicate.instance); } public MyojinOfCrypticDreams(UUID ownderId, CardSetInfo setInfo) { @@ -63,17 +62,12 @@ public class MyojinOfCrypticDreams extends CardImpl { this.addAbility(ability); } - private MyojinOfCrypticDreams(final MyojinOfCrypticDreams card) { super(card); } + private MyojinOfCrypticDreams(final MyojinOfCrypticDreams card) { + super(card); + } @Override - public MyojinOfCrypticDreams copy() { return new MyojinOfCrypticDreams(this); } -} - -enum MyojinOfCrypticDreamsPredicate implements Predicate { - instance; - - @Override - public boolean apply(StackObject input, Game game) { - return input.isPermanent(game); + public MyojinOfCrypticDreams copy() { + return new MyojinOfCrypticDreams(this); } } diff --git a/Mage.Sets/src/mage/sets/DominariaUnited.java b/Mage.Sets/src/mage/sets/DominariaUnited.java index 787719a5fa..a9ea6354c1 100644 --- a/Mage.Sets/src/mage/sets/DominariaUnited.java +++ b/Mage.Sets/src/mage/sets/DominariaUnited.java @@ -88,6 +88,11 @@ public final class DominariaUnited extends ExpansionSet { cards.add(new SetCardInfo("Cut Down", 89, Rarity.UNCOMMON, mage.cards.c.CutDown.class)); cards.add(new SetCardInfo("Danitha, Benalia's Hope", 15, Rarity.RARE, mage.cards.d.DanithaBenaliasHope.class)); cards.add(new SetCardInfo("Deathbloom Gardener", 159, Rarity.COMMON, mage.cards.d.DeathbloomGardener.class)); + cards.add(new SetCardInfo("Defiler of Dreams", 46, Rarity.RARE, mage.cards.d.DefilerOfDreams.class)); + cards.add(new SetCardInfo("Defiler of Faith", 16, Rarity.RARE, mage.cards.d.DefilerOfFaith.class)); + cards.add(new SetCardInfo("Defiler of Flesh", 90, Rarity.RARE, mage.cards.d.DefilerOfFlesh.class)); + cards.add(new SetCardInfo("Defiler of Instinct", 119, Rarity.RARE, mage.cards.d.DefilerOfInstinct.class)); + cards.add(new SetCardInfo("Defiler of Vigor", 160, Rarity.RARE, mage.cards.d.DefilerOfVigor.class)); cards.add(new SetCardInfo("Destroy Evil", 17, Rarity.COMMON, mage.cards.d.DestroyEvil.class)); cards.add(new SetCardInfo("Djinn of the Fountain", 47, Rarity.UNCOMMON, mage.cards.d.DjinnOfTheFountain.class)); cards.add(new SetCardInfo("Drag to the Bottom", 91, Rarity.RARE, mage.cards.d.DragToTheBottom.class)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/DefilersTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/DefilersTest.java new file mode 100644 index 0000000000..0bd2e742ca --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/DefilersTest.java @@ -0,0 +1,118 @@ +package org.mage.test.cards.cost.additional; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class DefilersTest extends CardTestPlayerBase { + private static final String greenDefiler = "Defiler of Vigor"; + private static final String redDefiler = "Defiler of Instinct"; + private static final String bear = "Grizzly Bears"; + private static final String tusker = "Kalonian Tusker"; + private static final String goblin = "Scarwood Goblins"; + + @Test + public void testRegularAccept() { + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, greenDefiler); + addCard(Zone.HAND, playerA, bear); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear); + setChoice(playerA, true); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertLife(playerA, 20 - 2); + assertPermanentCount(playerA, bear, 1); + assertCounterCount(playerA, greenDefiler, CounterType.P1P1, 1); + assertTappedCount("Forest", true, 1); + } + + @Test + public void testRegularDecline() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, greenDefiler); + addCard(Zone.HAND, playerA, bear); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear); + setChoice(playerA, false); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertLife(playerA, 20); + assertPermanentCount(playerA, bear, 1); + assertCounterCount(playerA, greenDefiler, CounterType.P1P1, 1); + assertTappedCount("Forest", true, 2); + } + + @Test + public void testDoubleReduceOne() { + addCard(Zone.BATTLEFIELD, playerA, "Forest"); + addCard(Zone.BATTLEFIELD, playerA, greenDefiler, 2); + addCard(Zone.HAND, playerA, bear); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear); + setChoice(playerA, true); + setChoice(playerA, true); + setChoice(playerA, ""); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertLife(playerA, 20 - 2 - 2); + assertPermanentCount(playerA, bear, 1); + assertCounterCount(playerA, greenDefiler, CounterType.P1P1, 2); + assertTappedCount("Forest", true, 1); + } + + @Test + public void testDoubleReduceBoth() { + addCard(Zone.BATTLEFIELD, playerA, greenDefiler, 2); + addCard(Zone.HAND, playerA, tusker); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, tusker); + setChoice(playerA, true); + setChoice(playerA, true); + setChoice(playerA, ""); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertLife(playerA, 20 - 2 - 2); + assertPermanentCount(playerA, tusker, 1); + assertCounterCount(playerA, greenDefiler, CounterType.P1P1, 2); + } + + @Test + public void testTwoColors() { + addCard(Zone.BATTLEFIELD, playerA, greenDefiler); + addCard(Zone.BATTLEFIELD, playerA, redDefiler); + addCard(Zone.HAND, playerA, goblin); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, goblin); + setChoice(playerA, true); + setChoice(playerA, true); + setChoice(playerA, ""); + addTarget(playerA, playerB); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + + assertLife(playerA, 20 - 2 - 2); + assertLife(playerB, 20 - 1); + assertPermanentCount(playerA, goblin, 1); + assertCounterCount(playerA, greenDefiler, CounterType.P1P1, 1); + } +} diff --git a/Mage/src/main/java/mage/abilities/common/MayPay2LifeForColorAbility.java b/Mage/src/main/java/mage/abilities/common/MayPay2LifeForColorAbility.java new file mode 100644 index 0000000000..3069f722c9 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/MayPay2LifeForColorAbility.java @@ -0,0 +1,104 @@ +package mage.abilities.common; + +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.SpellAbility; +import mage.abilities.StaticAbility; +import mage.abilities.costs.Cost; +import mage.abilities.costs.common.PayLifeCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.cost.CostModificationEffectImpl; +import mage.cards.Card; +import mage.constants.CostModificationType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +/** + * @author TheElk801 + */ +public class MayPay2LifeForColorAbility extends StaticAbility { + + private final String rule; + + public MayPay2LifeForColorAbility(ObjectColor color) { + super(Zone.BATTLEFIELD, new MayPay2LifeEffect(color)); + this.rule = makeRule(color); + } + + private MayPay2LifeForColorAbility(final MayPay2LifeForColorAbility ability) { + super(ability); + this.rule = ability.rule; + } + + @Override + public MayPay2LifeForColorAbility copy() { + return new MayPay2LifeForColorAbility(this); + } + + @Override + public String getRule() { + return rule; + } + + private static String makeRule(ObjectColor color) { + return "As an additional cost to cast " + color.getDescription() + + " permanent spells, you may pay 2 life. Those spells cost {" + color + + "} less to cast if you paid life this way. This effect reduces only " + + "the amount of " + color.getDescription() + " mana you pay."; + } +} + +class MayPay2LifeEffect extends CostModificationEffectImpl { + + private final ObjectColor color; + private final ManaCosts manaCost; + + MayPay2LifeEffect(ObjectColor color) { + super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST); + this.color = color; + this.manaCost = new ManaCostsImpl<>("{" + color + "}"); + } + + private MayPay2LifeEffect(final MayPay2LifeEffect effect) { + super(effect); + this.color = effect.color; + this.manaCost = effect.manaCost; + } + + @Override + public MayPay2LifeEffect copy() { + return new MayPay2LifeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source, Ability abilityToModify) { + Player player = game.getPlayer(abilityToModify.getControllerId()); + Cost cost = new PayLifeCost(2); + if (!cost.canPay(abilityToModify, source, source.getControllerId(), game)) { + return true; + } + if (game.inCheckPlayableState() || ( + player.chooseUse(outcome, "Pay 2 life to reduce the cost by {" + color + "}?", source, game) + && cost.pay(abilityToModify, game, source, source.getControllerId(), true) + )) { + CardUtil.adjustCost((SpellAbility) abilityToModify, manaCost, false); + } + return true; + } + + @Override + public boolean applies(Ability abilityToModify, Ability source, Game game) { + if (!(abilityToModify instanceof SpellAbility) + || !abilityToModify.isControlledBy(source.getControllerId())) { + return false; + } + Card spellCard = ((SpellAbility) abilityToModify).getCharacteristics(game); + return spellCard.isPermanent(game) && spellCard.getColor(game).contains(color); + } +} diff --git a/Mage/src/main/java/mage/filter/common/FilterPermanentCard.java b/Mage/src/main/java/mage/filter/common/FilterPermanentCard.java index 9d76594bb0..fa53c021ac 100644 --- a/Mage/src/main/java/mage/filter/common/FilterPermanentCard.java +++ b/Mage/src/main/java/mage/filter/common/FilterPermanentCard.java @@ -1,13 +1,9 @@ - - package mage.filter.common; -import mage.constants.CardType; import mage.filter.FilterCard; -import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.PermanentPredicate; /** - * * @author Plopman */ public class FilterPermanentCard extends FilterCard { @@ -18,13 +14,7 @@ public class FilterPermanentCard extends FilterCard { public FilterPermanentCard(String name) { super(name); - this.add( - Predicates.or( - CardType.ARTIFACT.getPredicate(), - CardType.CREATURE.getPredicate(), - CardType.ENCHANTMENT.getPredicate(), - CardType.LAND.getPredicate(), - CardType.PLANESWALKER.getPredicate())); + this.add(PermanentPredicate.instance); } public FilterPermanentCard(final FilterPermanentCard filter) { diff --git a/Mage/src/main/java/mage/filter/predicate/mageobject/PermanentPredicate.java b/Mage/src/main/java/mage/filter/predicate/mageobject/PermanentPredicate.java new file mode 100644 index 0000000000..8fb15cfbf1 --- /dev/null +++ b/Mage/src/main/java/mage/filter/predicate/mageobject/PermanentPredicate.java @@ -0,0 +1,17 @@ +package mage.filter.predicate.mageobject; + +import mage.MageObject; +import mage.filter.predicate.Predicate; +import mage.game.Game; + +/** + * @author TheElk801 + */ +public enum PermanentPredicate implements Predicate { + instance; + + @Override + public boolean apply(MageObject input, Game game) { + return input.isPermanent(game); + } +}