Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Impending mechanic #12865

Merged
merged 4 commits into from
Sep 14, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/o/OverlordOfTheBalemurk.java
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/o/OverlordOfTheBoilerbilges.java
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/o/OverlordOfTheFloodpits.java
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/o/OverlordOfTheHauntwoods.java
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion Mage.Sets/src/mage/cards/o/OverlordOfTheMistmoors.java
Original file line number Diff line number Diff line change
Expand Up @@ -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)));
Expand Down
2 changes: 0 additions & 2 deletions Mage.Sets/src/mage/sets/DuskmournHouseOfHorror.java
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
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;

/**
* @author TheElk801
*/
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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably should use the name param rather than hardcoding

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops good catch thanks

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);
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);
assertHasImpending(hauntwoods, true);

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);
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading
Loading