implement [MH3] Emrakul, the World Anew

This commit is contained in:
Susucre 2024-04-30 11:34:52 +02:00
parent 8a07ff30b9
commit 00556c4cf6
4 changed files with 272 additions and 0 deletions

View file

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

View file

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

View file

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

View file

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