From 12323d7de3ae8b7f54959b60f029174510419ac7 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 13 Sep 2024 12:04:23 -0400 Subject: [PATCH 1/4] implement Impending mechanic --- .../mage/cards/o/OverlordOfTheBalemurk.java | 2 +- .../cards/o/OverlordOfTheBoilerbilges.java | 2 +- .../mage/cards/o/OverlordOfTheFloodpits.java | 2 +- .../mage/cards/o/OverlordOfTheHauntwoods.java | 2 +- .../mage/cards/o/OverlordOfTheMistmoors.java | 2 +- .../src/mage/sets/DuskmournHouseOfHorror.java | 2 - .../abilities/keyword/ImpendingAbility.java | 117 ++++++++++++++++-- 7 files changed, 113 insertions(+), 16 deletions(-) diff --git a/Mage.Sets/src/mage/cards/o/OverlordOfTheBalemurk.java b/Mage.Sets/src/mage/cards/o/OverlordOfTheBalemurk.java index 7c8cff9fcf5c..607a656af694 100644 --- a/Mage.Sets/src/mage/cards/o/OverlordOfTheBalemurk.java +++ b/Mage.Sets/src/mage/cards/o/OverlordOfTheBalemurk.java @@ -36,7 +36,7 @@ public OverlordOfTheBalemurk(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(5); // Impending 5--{1}{B} - this.addAbility(new ImpendingAbility("{1}{B}", 5)); + this.addAbility(new ImpendingAbility(5, "{1}{B}")); // Whenever Overlord of the Balemurk enters or attacks, mill four cards, then you may return a non-Avatar creature card or a planeswalker card from your graveyard to your hand. Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new MillCardsControllerEffect(4)); diff --git a/Mage.Sets/src/mage/cards/o/OverlordOfTheBoilerbilges.java b/Mage.Sets/src/mage/cards/o/OverlordOfTheBoilerbilges.java index 9a1850c0e0bc..fd7a76736c88 100644 --- a/Mage.Sets/src/mage/cards/o/OverlordOfTheBoilerbilges.java +++ b/Mage.Sets/src/mage/cards/o/OverlordOfTheBoilerbilges.java @@ -27,7 +27,7 @@ public OverlordOfTheBoilerbilges(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(5); // Impending 4--{2}{R}{R} - this.addAbility(new ImpendingAbility("{2}{R}{R}")); + this.addAbility(new ImpendingAbility(4, "{2}{R}{R}")); // Whenever Overlord of the Boilerbilges enters or attacks, it deals 4 damage to any target. Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new DamageTargetEffect(4)); diff --git a/Mage.Sets/src/mage/cards/o/OverlordOfTheFloodpits.java b/Mage.Sets/src/mage/cards/o/OverlordOfTheFloodpits.java index ac54cf2aad6b..45bd2989f433 100644 --- a/Mage.Sets/src/mage/cards/o/OverlordOfTheFloodpits.java +++ b/Mage.Sets/src/mage/cards/o/OverlordOfTheFloodpits.java @@ -26,7 +26,7 @@ public OverlordOfTheFloodpits(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(3); // Impending 4--{1}{U}{U} - this.addAbility(new ImpendingAbility("{1}{U}{U}")); + this.addAbility(new ImpendingAbility(4, "{1}{U}{U}")); // Flying this.addAbility(FlyingAbility.getInstance()); diff --git a/Mage.Sets/src/mage/cards/o/OverlordOfTheHauntwoods.java b/Mage.Sets/src/mage/cards/o/OverlordOfTheHauntwoods.java index 63df6a010efd..e9c437059f59 100644 --- a/Mage.Sets/src/mage/cards/o/OverlordOfTheHauntwoods.java +++ b/Mage.Sets/src/mage/cards/o/OverlordOfTheHauntwoods.java @@ -26,7 +26,7 @@ public OverlordOfTheHauntwoods(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(5); // Impending 4--{1}{G}{G} - this.addAbility(new ImpendingAbility("{1}{G}{G}")); + this.addAbility(new ImpendingAbility(4, "{1}{G}{G}")); // Whenever Overlord of the Hauntwoods enters or attacks, create a tapped colorless land token named Everywhere that is every basic land type. this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility( diff --git a/Mage.Sets/src/mage/cards/o/OverlordOfTheMistmoors.java b/Mage.Sets/src/mage/cards/o/OverlordOfTheMistmoors.java index c3124d29013a..8a8309acc0a7 100644 --- a/Mage.Sets/src/mage/cards/o/OverlordOfTheMistmoors.java +++ b/Mage.Sets/src/mage/cards/o/OverlordOfTheMistmoors.java @@ -26,7 +26,7 @@ public OverlordOfTheMistmoors(UUID ownerId, CardSetInfo setInfo) { this.toughness = new MageInt(6); // Impending 4--{2}{W}{W} - this.addAbility(new ImpendingAbility("{2}{W}{W}")); + this.addAbility(new ImpendingAbility(4, "{2}{W}{W}")); // Whenever Overlord of the Mistmoors enters or attacks, create two 2/1 white Insect creature tokens with flying. this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new CreateTokenEffect(new InsectWhiteToken(), 2))); diff --git a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java index db9ccba49145..0229648bd004 100644 --- a/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java +++ b/Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java @@ -218,7 +218,5 @@ private DuskmournHouseOfHorror() { cards.add(new SetCardInfo("Winter, Misanthropic Guide", 240, Rarity.RARE, mage.cards.w.WinterMisanthropicGuide.class)); cards.add(new SetCardInfo("Withering Torment", 124, Rarity.UNCOMMON, mage.cards.w.WitheringTorment.class)); cards.add(new SetCardInfo("Zimone, All-Questioning", 241, Rarity.RARE, mage.cards.z.ZimoneAllQuestioning.class)); - - cards.removeIf(setCardInfo -> setCardInfo.getName().startsWith("Overlord")); } } diff --git a/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java b/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java index a6594965d075..58a1d9d44e15 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java @@ -1,12 +1,32 @@ package mage.abilities.keyword; import mage.abilities.Ability; +import mage.abilities.common.BeginningOfEndStepTriggeredAbility; +import mage.abilities.common.EntersBattlefieldAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.AlternativeSourceCostsImpl; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.common.AddContinuousEffectToGame; +import mage.abilities.effects.common.counter.AddCountersSourceEffect; +import mage.abilities.effects.common.counter.RemoveCounterSourceEffect; +import mage.constants.*; +import mage.counters.CounterType; import mage.game.Game; +import mage.game.permanent.Permanent; import mage.util.CardUtil; +import java.util.stream.Collectors; + /** - * TODO: Implement this + * "Impending N–[cost]" is a keyword that represents multiple abilities. + * The official rules are as follows: + * (a) You may choose to pay [cost] rather than pay this spell's mana cost. + * (b) If you chose to pay this spell's impending cost, it enters the battlefield with N time counters on it. + * (c) As long as this permanent has a time counter on it, if it was cast for its impending cost, it's not a creature. + * (d) At the beginning of your end step, if this permanent was cast for its impending cost, remove a time counter from it. Then if it has no time counters on it, it loses impending. * * @author TheElk801 */ @@ -16,14 +36,24 @@ public class ImpendingAbility extends AlternativeSourceCostsImpl { private static final String IMPENDING_REMINDER = "If you cast this spell for its impending cost, " + "it enters with %s time counters and isn't a creature until the last is removed. " + "At the beginning of your end step, remove a time counter from it."; + private static final Condition counterCondition = new SourceHasCounterCondition(CounterType.TIME, 0, 0); - public ImpendingAbility(String manaString) { - this(manaString, 4); - } - - public ImpendingAbility(String manaString, int amount) { + public ImpendingAbility(int amount, String manaString) { super(IMPENDING_KEYWORD + ' ' + amount, String.format(IMPENDING_REMINDER, CardUtil.numberToText(amount)), manaString); this.setRuleAtTheTop(true); + this.addSubAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect( + new AddCountersSourceEffect(CounterType.TIME.createInstance(amount)), ImpendingCondition.instance, "" + ), "").setRuleVisible(false)); + this.addSubAbility(new SimpleStaticAbility(new ImpendingAbilityTypeEffect()).setRuleVisible(false)); + Ability ability = new BeginningOfEndStepTriggeredAbility( + new RemoveCounterSourceEffect(CounterType.TIME.createInstance()), + TargetController.YOU, ImpendingCondition.instance, false + ); + ability.addEffect(new ConditionalOneShotEffect( + new AddContinuousEffectToGame(new ImpendingAbilityRemoveEffect()), + counterCondition, "Then if it has no time counters on it, it loses impending" + )); + this.addSubAbility(ability.setRuleVisible(false)); } private ImpendingAbility(final ImpendingAbility ability) { @@ -35,12 +65,81 @@ public ImpendingAbility copy() { return new ImpendingAbility(this); } + public static String getActivationKey() { + return getActivationKey(IMPENDING_KEYWORD); + } +} + +enum ImpendingCondition implements Condition { + instance; + + @Override + public boolean apply(Game game, Ability source) { + return CardUtil.checkSourceCostsTagExists(game, source, ImpendingAbility.getActivationKey()); + } +} + +class ImpendingAbilityTypeEffect extends ContinuousEffectImpl { + + ImpendingAbilityTypeEffect() { + super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Detriment); + staticText = "As long as this permanent has a time counter on it, if it was cast for its impending cost, it's not a creature."; + } + + private ImpendingAbilityTypeEffect(final ImpendingAbilityTypeEffect effect) { + super(effect); + } + @Override - public boolean isAvailable(Ability source, Game game) { + public ImpendingAbilityTypeEffect copy() { + return new ImpendingAbilityTypeEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + if (!ImpendingCondition.instance.apply(game, source)) { + return false; + } + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent.getCounters(game).getCount(CounterType.TIME) < 1) { + return false; + } + permanent.removeCardType(game, CardType.CREATURE); + permanent.removeAllCreatureTypes(game); return true; } +} - public static String getActivationKey() { - return getActivationKey(IMPENDING_KEYWORD); +class ImpendingAbilityRemoveEffect extends ContinuousEffectImpl { + + ImpendingAbilityRemoveEffect() { + super(Duration.Custom, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.LoseAbility); + } + + private ImpendingAbilityRemoveEffect(final ImpendingAbilityRemoveEffect effect) { + super(effect); + } + + @Override + public ImpendingAbilityRemoveEffect copy() { + return new ImpendingAbilityRemoveEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = source.getSourcePermanentIfItStillExists(game); + if (permanent == null) { + discard(); + return false; + } + permanent.removeAbilities( + permanent + .getAbilities(game) + .stream() + .filter(ImpendingAbility.class::isInstance) + .collect(Collectors.toList()), + source.getSourceId(), game + ); + return true; } } From 1f456f34a0b583d4bb523279ef9f46ae531a3bc8 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 13 Sep 2024 12:36:09 -0400 Subject: [PATCH 2/4] add initial test --- .../abilities/keywords/ImpendingTest.java | 60 +++++++++++++++++++ .../costs/AlternativeSourceCostsImpl.java | 6 +- .../abilities/keyword/ImpendingAbility.java | 3 +- 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java new file mode 100644 index 000000000000..7e2ad85afd7b --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java @@ -0,0 +1,60 @@ +package org.mage.test.cards.abilities.keywords; + +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.SubType; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class ImpendingTest extends CardTestPlayerBase { + + private static final String hauntwoods = "Overlord of the Hauntwoods"; + + @Test + public void testCastRegular() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.HAND, playerA, hauntwoods); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hauntwoods); + setChoice(playerA, "Cast with no"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, hauntwoods, 1); + assertType(hauntwoods, CardType.ENCHANTMENT, true); + assertType(hauntwoods, CardType.CREATURE, true); + assertSubtype(hauntwoods, SubType.AVATAR); + assertSubtype(hauntwoods, SubType.HORROR); + assertCounterCount(playerA, hauntwoods, CounterType.TIME, 0); + assertPowerToughness(playerA, hauntwoods, 6, 5); + + assertPermanentCount(playerA, "Everywhere", 1); + } + + @Test + public void testCastImpending() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.HAND, playerA, hauntwoods); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hauntwoods); + setChoice(playerA, "Cast with Impending"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, hauntwoods, 1); + assertType(hauntwoods, CardType.ENCHANTMENT, true); + assertType(hauntwoods, CardType.CREATURE, false); + assertCounterCount(playerA, hauntwoods, CounterType.TIME, 4); + + assertPermanentCount(playerA, "Everywhere", 1); + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/AlternativeSourceCostsImpl.java b/Mage/src/main/java/mage/abilities/costs/AlternativeSourceCostsImpl.java index 0f947e5568ec..7657a0a336d8 100644 --- a/Mage/src/main/java/mage/abilities/costs/AlternativeSourceCostsImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/AlternativeSourceCostsImpl.java @@ -30,11 +30,15 @@ protected AlternativeSourceCostsImpl(String name, String reminderText, String ma } protected AlternativeSourceCostsImpl(String name, String reminderText, Cost cost) { + this(name, reminderText, cost, name); + } + + protected AlternativeSourceCostsImpl(String name, String reminderText, Cost cost, String activationKey) { super(Zone.ALL, null); this.name = name; this.reminderText = reminderText; this.alternativeCost = new AlternativeCostImpl<>(name, reminderText, cost); - this.activationKey = getActivationKey(name); + this.activationKey = getActivationKey(activationKey); } protected AlternativeSourceCostsImpl(final AlternativeSourceCostsImpl ability) { diff --git a/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java b/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java index 58a1d9d44e15..4d02211948b6 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ImpendingAbility.java @@ -7,6 +7,7 @@ import mage.abilities.condition.Condition; import mage.abilities.condition.common.SourceHasCounterCondition; import mage.abilities.costs.AlternativeSourceCostsImpl; +import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.effects.common.AddContinuousEffectToGame; @@ -39,7 +40,7 @@ public class ImpendingAbility extends AlternativeSourceCostsImpl { private static final Condition counterCondition = new SourceHasCounterCondition(CounterType.TIME, 0, 0); public ImpendingAbility(int amount, String manaString) { - super(IMPENDING_KEYWORD + ' ' + amount, String.format(IMPENDING_REMINDER, CardUtil.numberToText(amount)), manaString); + super(IMPENDING_KEYWORD + ' ' + amount, String.format(IMPENDING_REMINDER, CardUtil.numberToText(amount)), new ManaCostsImpl<>(manaString), IMPENDING_KEYWORD); this.setRuleAtTheTop(true); this.addSubAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect( new AddCountersSourceEffect(CounterType.TIME.createInstance(amount)), ImpendingCondition.instance, "" From a27aa82a19926dce9b22c49391957c4c55e274dd Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 13 Sep 2024 13:00:03 -0400 Subject: [PATCH 3/4] add more tests --- .../abilities/keywords/ImpendingTest.java | 166 ++++++++++++++++++ 1 file changed, 166 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java index 7e2ad85afd7b..0bde450d995b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java @@ -1,10 +1,13 @@ package org.mage.test.cards.abilities.keywords; +import mage.abilities.keyword.ImpendingAbility; import mage.constants.CardType; import mage.constants.PhaseStep; import mage.constants.SubType; import mage.constants.Zone; import mage.counters.CounterType; +import mage.game.permanent.Permanent; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -15,6 +18,14 @@ public class ImpendingTest extends CardTestPlayerBase { private static final String hauntwoods = "Overlord of the Hauntwoods"; + public void assertHasImpending(String name, boolean hasAbility) { + Permanent permanent = getPermanent(hauntwoods); + Assert.assertEquals( + "Should" + (hasAbility ? "" : "n't") + " have Impending ability", + hasAbility, permanent.getAbilities(currentGame).containsClass(ImpendingAbility.class) + ); + } + @Test public void testCastRegular() { addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); @@ -34,6 +45,7 @@ public void testCastRegular() { assertSubtype(hauntwoods, SubType.HORROR); assertCounterCount(playerA, hauntwoods, CounterType.TIME, 0); assertPowerToughness(playerA, hauntwoods, 6, 5); + assertHasImpending(hauntwoods, true); assertPermanentCount(playerA, "Everywhere", 1); } @@ -54,6 +66,160 @@ public void testCastImpending() { assertType(hauntwoods, CardType.ENCHANTMENT, true); assertType(hauntwoods, CardType.CREATURE, false); assertCounterCount(playerA, hauntwoods, CounterType.TIME, 4); + assertHasImpending(hauntwoods, true); + + assertPermanentCount(playerA, "Everywhere", 1); + } + + @Test + public void testImpendingRemoveCounter() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.HAND, playerA, hauntwoods); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hauntwoods); + setChoice(playerA, "Cast with Impending"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, hauntwoods, 1); + assertType(hauntwoods, CardType.ENCHANTMENT, true); + assertType(hauntwoods, CardType.CREATURE, false); + assertCounterCount(playerA, hauntwoods, CounterType.TIME, 4 - 1); + assertHasImpending(hauntwoods, true); + + assertPermanentCount(playerA, "Everywhere", 1); + } + + @Test + public void testCastImpendingRemoveAllCounters() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.HAND, playerA, hauntwoods); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hauntwoods); + setChoice(playerA, "Cast with Impending"); + + setStrictChooseMode(true); + setStopAt(8, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, hauntwoods, 1); + assertType(hauntwoods, CardType.ENCHANTMENT, true); + assertType(hauntwoods, CardType.CREATURE, true); + assertSubtype(hauntwoods, SubType.AVATAR); + assertSubtype(hauntwoods, SubType.HORROR); + assertCounterCount(playerA, hauntwoods, CounterType.TIME, 0); + assertPowerToughness(playerA, hauntwoods, 6, 5); + assertHasImpending(hauntwoods, false); + + assertPermanentCount(playerA, "Everywhere", 1); + } + + private static final String hexmage = "Vampire Hexmage"; + + @Test + public void testCastImpendingHexmage() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.BATTLEFIELD, playerA, hexmage); + addCard(Zone.HAND, playerA, hauntwoods); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hauntwoods); + setChoice(playerA, "Cast with Impending"); + + activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "Sacrifice", hauntwoods); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, hauntwoods, 1); + assertType(hauntwoods, CardType.ENCHANTMENT, true); + assertType(hauntwoods, CardType.CREATURE, true); + assertSubtype(hauntwoods, SubType.AVATAR); + assertSubtype(hauntwoods, SubType.HORROR); + assertCounterCount(playerA, hauntwoods, CounterType.TIME, 0); + assertPowerToughness(playerA, hauntwoods, 6, 5); + assertHasImpending(hauntwoods, true); + + assertPermanentCount(playerA, "Everywhere", 1); + } + + @Test + public void testCastImpendingHexmageNextTurn() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.BATTLEFIELD, playerA, hexmage); + addCard(Zone.HAND, playerA, hauntwoods); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hauntwoods); + setChoice(playerA, "Cast with Impending"); + + activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "Sacrifice", hauntwoods); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, hauntwoods, 1); + assertType(hauntwoods, CardType.ENCHANTMENT, true); + assertType(hauntwoods, CardType.CREATURE, true); + assertSubtype(hauntwoods, SubType.AVATAR); + assertSubtype(hauntwoods, SubType.HORROR); + assertCounterCount(playerA, hauntwoods, CounterType.TIME, 0); + assertPowerToughness(playerA, hauntwoods, 6, 5); + assertHasImpending(hauntwoods, false); + + assertPermanentCount(playerA, "Everywhere", 1); + } + + private static final String solemnity = "Solemnity"; + + @Test + public void testCastImpendingSolemnity() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.BATTLEFIELD, playerA, solemnity); + addCard(Zone.HAND, playerA, hauntwoods); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hauntwoods); + setChoice(playerA, "Cast with Impending"); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, hauntwoods, 1); + assertType(hauntwoods, CardType.ENCHANTMENT, true); + assertType(hauntwoods, CardType.CREATURE, true); + assertSubtype(hauntwoods, SubType.AVATAR); + assertSubtype(hauntwoods, SubType.HORROR); + assertCounterCount(playerA, hauntwoods, CounterType.TIME, 0); + assertPowerToughness(playerA, hauntwoods, 6, 5); + assertHasImpending(hauntwoods, true); + + assertPermanentCount(playerA, "Everywhere", 1); + } + + @Test + public void testCastImpendingSolemnityNextTurn() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + addCard(Zone.BATTLEFIELD, playerA, solemnity); + addCard(Zone.HAND, playerA, hauntwoods); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, hauntwoods); + setChoice(playerA, "Cast with Impending"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertPermanentCount(playerA, hauntwoods, 1); + assertType(hauntwoods, CardType.ENCHANTMENT, true); + assertType(hauntwoods, CardType.CREATURE, true); + assertSubtype(hauntwoods, SubType.AVATAR); + assertSubtype(hauntwoods, SubType.HORROR); + assertCounterCount(playerA, hauntwoods, CounterType.TIME, 0); + assertPowerToughness(playerA, hauntwoods, 6, 5); + assertHasImpending(hauntwoods, false); assertPermanentCount(playerA, "Everywhere", 1); } From 85d84d02b9da0546f781e38d04143b5b195c7947 Mon Sep 17 00:00:00 2001 From: theelk801 Date: Fri, 13 Sep 2024 20:44:26 -0400 Subject: [PATCH 4/4] small fix --- .../org/mage/test/cards/abilities/keywords/ImpendingTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java index 0bde450d995b..33b65e5ec3d9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ImpendingTest.java @@ -19,7 +19,7 @@ public class ImpendingTest extends CardTestPlayerBase { private static final String hauntwoods = "Overlord of the Hauntwoods"; public void assertHasImpending(String name, boolean hasAbility) { - Permanent permanent = getPermanent(hauntwoods); + Permanent permanent = getPermanent(name); Assert.assertEquals( "Should" + (hasAbility ? "" : "n't") + " have Impending ability", hasAbility, permanent.getAbilities(currentGame).containsClass(ImpendingAbility.class)