diff --git a/Mage.Sets/src/mage/cards/e/EmrakulTheWorldAnew.java b/Mage.Sets/src/mage/cards/e/EmrakulTheWorldAnew.java new file mode 100644 index 00000000000..c9c8baa7174 --- /dev/null +++ b/Mage.Sets/src/mage/cards/e/EmrakulTheWorldAnew.java @@ -0,0 +1,97 @@ +package mage.cards.e; + +import mage.MageInt; +import mage.MageObjectReference; +import mage.abilities.Ability; +import mage.abilities.common.LeavesBattlefieldTriggeredAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.CastSourceTriggeredAbility; +import mage.abilities.effects.common.SacrificeAllControllerEffect; +import mage.abilities.effects.common.continuous.GainControlAllControlledTargetEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.MadnessAbility; +import mage.abilities.keyword.ProtectionAbility; +import mage.cards.CardImpl; +import mage.cards.CardSetInfo; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.filter.FilterPermanent; +import mage.filter.FilterSpell; +import mage.filter.StaticFilters; +import mage.filter.predicate.Predicate; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.TargetPlayer; +import mage.watchers.common.SpellsCastWatcher; + +import java.util.UUID; + +/** + * @author Susucr + */ +public final class EmrakulTheWorldAnew extends CardImpl { + + private static final FilterSpell filterSpell = new FilterSpell("spells"); + private static final FilterPermanent filterPermanent = new FilterPermanent(); + + static { + filterPermanent.add(EmrakulTheWorldAnewPredicate.instance); + } + + public EmrakulTheWorldAnew(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{12}"); + + this.supertype.add(SuperType.LEGENDARY); + this.subtype.add(SubType.ELDRAZI); + this.power = new MageInt(12); + this.toughness = new MageInt(12); + + // When you cast this spell, gain control of all creatures target player controls. + Ability ability = new CastSourceTriggeredAbility( + new GainControlAllControlledTargetEffect(StaticFilters.FILTER_PERMANENT_CREATURES) + ); + ability.addTarget(new TargetPlayer()); + this.addAbility(ability); + + // Flying + this.addAbility(FlyingAbility.getInstance()); + + // protection from spells and from permanents that were cast this turn + this.addAbility(new ProtectionAbility(filterSpell)); + this.addAbility(new ProtectionAbility(filterPermanent) + .setText("Protection from permanents that were cast this turn")); + + // When Emrakul, the World Anew leaves the battlefield, sacrifice all creatures you control. + this.addAbility(new LeavesBattlefieldTriggeredAbility(new SacrificeAllControllerEffect(StaticFilters.FILTER_PERMANENT_CREATURES), false)); + + // Madness--Pay six {C}. + this.addAbility(new MadnessAbility(new ManaCostsImpl<>("{C}{C}{C}{C}{C}{C}").setText("—Pay six {C}."))); + + } + + private EmrakulTheWorldAnew(final EmrakulTheWorldAnew card) { + super(card); + } + + @Override + public EmrakulTheWorldAnew copy() { + return new EmrakulTheWorldAnew(this); + } +} + +/** + * permanent was cast this turn + */ +enum EmrakulTheWorldAnewPredicate implements Predicate { + instance; + + @Override + public boolean apply(Permanent input, Game game) { + SpellsCastWatcher watcher = game.getState().getWatcher(SpellsCastWatcher.class); + MageObjectReference mor = new MageObjectReference(input, game, -1); + return watcher != null && watcher + .getAllSpellsCastThisTurn() + .anyMatch(spell -> mor.refersTo(spell, game)); + } +} diff --git a/Mage.Sets/src/mage/sets/ModernHorizons3.java b/Mage.Sets/src/mage/sets/ModernHorizons3.java index a8249702e74..7cc7eec5bf5 100644 --- a/Mage.Sets/src/mage/sets/ModernHorizons3.java +++ b/Mage.Sets/src/mage/sets/ModernHorizons3.java @@ -24,6 +24,7 @@ public final class ModernHorizons3 extends ExpansionSet { cards.add(new SetCardInfo("Ajani, Nacatl Avenger", 237, Rarity.MYTHIC, mage.cards.a.AjaniNacatlAvenger.class)); cards.add(new SetCardInfo("Ajani, Nacatl Pariah", 237, Rarity.MYTHIC, mage.cards.a.AjaniNacatlPariah.class)); cards.add(new SetCardInfo("Bloodstained Mire", 216, Rarity.RARE, mage.cards.b.BloodstainedMire.class)); + cards.add(new SetCardInfo("Emrakul, the World Anew", 6, Rarity.MYTHIC, mage.cards.e.EmrakulTheWorldAnew.class)); cards.add(new SetCardInfo("Flare of Cultivation", 154, Rarity.RARE, mage.cards.f.FlareOfCultivation.class)); cards.add(new SetCardInfo("Flooded Strand", 220, Rarity.RARE, mage.cards.f.FloodedStrand.class)); cards.add(new SetCardInfo("Forest", 308, Rarity.LAND, mage.cards.basiclands.Forest.class, NON_FULL_USE_VARIOUS)); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/EmrakulTheWorldAnewTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/EmrakulTheWorldAnewTest.java new file mode 100644 index 00000000000..a458544bc79 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/mh3/EmrakulTheWorldAnewTest.java @@ -0,0 +1,163 @@ +package org.mage.test.cards.single.mh3; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author Susucr + */ +public class EmrakulTheWorldAnewTest extends CardTestPlayerBase { + + /** + * {@link mage.cards.e.EmrakulTheWorldAnew Emrakul, the World Anew} {12} + * Legendary Creature — Eldrazi + * When you cast this spell, gain control of all creatures target player controls. + * Flying, protection from spells and from permanents that were cast this turn + * When Emrakul, the World Anew leaves the battlefield, sacrifice all creatures you control. + * Madness—Pay six {C}. + * 12/12 + */ + private static final String emrakul = "Emrakul, the World Anew"; + + @Test + public void test_Madness_And_Triggers() { + setStrictChooseMode(true); + + addCard(Zone.HAND, playerA, emrakul, 1); + addCard(Zone.HAND, playerA, "One with Nothing"); // Discard your hand + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Wastes", 6); + + addCard(Zone.HAND, playerB, "Diabolic Edict"); // Target player sacrifices a creature. + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerB, "Memnite", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "One with Nothing", true); + setChoice(playerA, true); // Yes to madness trigger + addTarget(playerA, playerB); // for cast trigger + + checkPermanentCount("Has gained control of Memnites", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Memnite", 2); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Diabolic Edict", playerA); + setChoice(playerA, emrakul); // choose to sacrifice emrakul, will trigger leave effect + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, 2); // One with Nothing + Emrakul. + assertGraveyardCount(playerB, "Memnite", 2); + } + + @Test + public void test_Protection_Spells() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerB, emrakul); + + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + addCard(Zone.HAND, playerA, "Murder"); + addCard(Zone.HAND, playerA, "Pyroclasm"); + + checkPlayableAbility("Protected from Murder", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Murder", false); + checkPlayableAbility("Pyroclasm can be cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Pyroclasm", true); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pyroclasm"); + // Cast, but deals no damage to Emrakul + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertDamageReceived(playerB, emrakul, 0); // protection from spell damage + } + + @Test + public void test_Protection_Pacifism() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerB, emrakul); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.HAND, playerA, "Pacifism"); + + // TODO: Investigate. Pacifism should not be castable at all. Probably more an Aura bug than an Emrakul bug + checkPlayableAbility("Protected from Pacifism", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Pacifism", true); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pacifism", emrakul); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + try { + execute(); + Assert.fail("should have failed on casting Pacifism"); + } catch (AssertionError error) { + Assert.assertEquals("Can't find ability to activate command: Cast Pacifism$target=Emrakul, the World Anew", error.getMessage()); + } + } + + @Test + public void test_Protection_CastThisTurn() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerB, emrakul); + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4); + addCard(Zone.HAND, playerA, "Chandra, Torch of Defiance"); // −3: Chandra, Torch of Defiance deals 4 damage to target creature. + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Chandra, Torch of Defiance", true); + + checkPlayableAbility("Protected from Chandra the turn it was cast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "-3", false); + checkPlayableAbility("No longer protected the turn after", 3, PhaseStep.PRECOMBAT_MAIN, playerA, "-3", true); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "-3", emrakul); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertDamageReceived(playerB, emrakul, 4); + } + + @Test + public void test_NoProtection_IfNotCast() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerB, emrakul); + + addCard(Zone.BATTLEFIELD, playerA, "Badlands", 5); + addCard(Zone.HAND, playerA, "Flametongue Kavu"); // When Flametongue Kavu enters the battlefield, it deals 4 damage to target creature. + addCard(Zone.HAND, playerA, "Reanimate"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flametongue Kavu", true); + addTarget(playerA, "Flametongue Kavu"); // only legal target. + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Reanimate", "Flametongue Kavu"); + addTarget(playerA, emrakul); // since Kavu enters from graveyard, it is valid to target emrakul + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertDamageReceived(playerB, emrakul, 4); + } + + @Test + public void test_CanBeBlockedByTokens() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerB, emrakul); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + addCard(Zone.HAND, playerA, "Midnight Haunting"); // Create two 1/1 white Spirit creature tokens with flying. + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Midnight Haunting"); + attack(2, playerB, emrakul, playerA); + block(2, playerA, "Spirit Token", emrakul); + + setStopAt(2, PhaseStep.END_COMBAT); + execute(); + + assertDamageReceived(playerB, emrakul, 1); + assertLife(playerA, 20); + } +} diff --git a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java index f60258fbf2e..913b9d7c9d1 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ProtectionAbility.java @@ -30,6 +30,7 @@ public class ProtectionAbility extends StaticAbility { protected boolean doesntRemoveControlled; protected ObjectColor fromColor; protected UUID auraIdNotToBeRemoved; // defines an Aura objectId that will not be removed from this protection ability + private String staticText; public ProtectionAbility(Filter filter) { super(Zone.BATTLEFIELD, null); @@ -39,6 +40,7 @@ public class ProtectionAbility extends StaticAbility { this.doesntRemoveControlled = false; this.fromColor = new ObjectColor(); this.auraIdNotToBeRemoved = null; + this.staticText = null; } protected ProtectionAbility(final ProtectionAbility ability) { @@ -49,6 +51,7 @@ public class ProtectionAbility extends StaticAbility { this.doesntRemoveControlled = ability.doesntRemoveControlled; this.fromColor = ability.fromColor; this.auraIdNotToBeRemoved = ability.auraIdNotToBeRemoved; + this.staticText = ability.staticText; } public static ProtectionAbility from(ObjectColor color) { @@ -75,10 +78,18 @@ public class ProtectionAbility extends StaticAbility { @Override public String getRule() { + if (this.staticText != null && !this.staticText.isEmpty()) { + return this.staticText; + } return (flavorWord == null ? "protection from " : CardUtil.italicizeWithEmDash(flavorWord) + "Protection from ") + filter.getMessage() + (removeAuras ? "" : ". This effect doesn't remove Auras."); } + public ProtectionAbility setText(String text) { + this.staticText = text; + return this; + } + public boolean canTarget(MageObject source, Game game) { // TODO: need research, protection ability can be bugged with aura and aura permanents, spells (see below)