From 2fec825523fb7e0ffdff8dbbc4cdd32c7cb0f85a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 18 Sep 2020 16:15:21 +0200 Subject: [PATCH] * Some standardisation of dies trigger handling (fixes #7063 Midnight Reaper triggers when dies face down). --- .../src/mage/cards/e/ExpeditionDiviner.java | 4 +- .../src/mage/cards/f/FirebladeCharger.java | 4 +- .../mage/cards/g/GrakmawSkyclaveRavager.java | 4 +- .../src/mage/cards/g/GuulDrazMucklord.java | 4 +- Mage.Sets/src/mage/cards/l/LeylineTyrant.java | 4 +- Mage.Sets/src/mage/cards/s/ShrivelingRot.java | 1 + Mage.Sets/src/mage/cards/t/TimeToFeed.java | 4 +- Mage.Sets/src/mage/cards/t/TroveWarden.java | 4 +- .../abilities/keywords/ManifestTest.java | 1077 +++++++++-------- .../test/cards/facedown/GrimHaruspexTest.java | 6 + .../mage/test/cards/facedown/TriggerTest.java | 62 + .../mage/abilities/TriggeredAbilityImpl.java | 35 + .../common/DiesCreatureTriggeredAbility.java | 6 + .../common/DiesSourceTriggeredAbility.java | 49 +- ...reatureOrPlaneswalkerTriggeredAbility.java | 43 +- ...ThisOrAnotherCreatureTriggeredAbility.java | 44 +- .../common/DiesTriggeredAbility.java | 79 -- 17 files changed, 720 insertions(+), 710 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/facedown/TriggerTest.java delete mode 100644 Mage/src/main/java/mage/abilities/common/DiesTriggeredAbility.java diff --git a/Mage.Sets/src/mage/cards/e/ExpeditionDiviner.java b/Mage.Sets/src/mage/cards/e/ExpeditionDiviner.java index 96c0b20e78..bc6c96b825 100644 --- a/Mage.Sets/src/mage/cards/e/ExpeditionDiviner.java +++ b/Mage.Sets/src/mage/cards/e/ExpeditionDiviner.java @@ -1,7 +1,6 @@ package mage.cards.e; import mage.MageInt; -import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; @@ -18,6 +17,7 @@ import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.permanent.AnotherPredicate; import java.util.UUID; +import mage.abilities.common.DiesSourceTriggeredAbility; /** * @author TheElk801 @@ -45,7 +45,7 @@ public final class ExpeditionDiviner extends CardImpl { // As long as you control another Wizard, Expedition Diviner has "When this creature dies, draw a card." this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( - new GainAbilitySourceEffect(new DiesTriggeredAbility(new DrawCardSourceControllerEffect(1))), + new GainAbilitySourceEffect(new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(1))), condition, "As long as you control another Wizard, {this} has \"When this creature dies, draw a card.\"" ))); } diff --git a/Mage.Sets/src/mage/cards/f/FirebladeCharger.java b/Mage.Sets/src/mage/cards/f/FirebladeCharger.java index 51c75ee5c7..627a670e1a 100644 --- a/Mage.Sets/src/mage/cards/f/FirebladeCharger.java +++ b/Mage.Sets/src/mage/cards/f/FirebladeCharger.java @@ -2,7 +2,6 @@ package mage.cards.f; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.EquippedSourceCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -19,6 +18,7 @@ import mage.constants.SubType; import mage.target.common.TargetAnyTarget; import java.util.UUID; +import mage.abilities.common.DiesSourceTriggeredAbility; /** * @author TheElk801 @@ -42,7 +42,7 @@ public final class FirebladeCharger extends CardImpl { ))); // When Fireblade Charger dies, it deals damage equal to its power to any target. - Ability ability = new DiesTriggeredAbility( + Ability ability = new DiesSourceTriggeredAbility( new DamageTargetEffect(xValue).setText("it deals damage equal to its power to any target") ); ability.addTarget(new TargetAnyTarget()); diff --git a/Mage.Sets/src/mage/cards/g/GrakmawSkyclaveRavager.java b/Mage.Sets/src/mage/cards/g/GrakmawSkyclaveRavager.java index d8eac4f9b1..fc155c4869 100644 --- a/Mage.Sets/src/mage/cards/g/GrakmawSkyclaveRavager.java +++ b/Mage.Sets/src/mage/cards/g/GrakmawSkyclaveRavager.java @@ -3,7 +3,6 @@ package mage.cards.g; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DiesCreatureTriggeredAbility; -import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; @@ -22,6 +21,7 @@ import mage.game.permanent.Permanent; import mage.game.permanent.token.GrakmawSkyclaveRavagerToken; import java.util.UUID; +import mage.abilities.common.DiesSourceTriggeredAbility; /** * @author TheElk801 @@ -59,7 +59,7 @@ public final class GrakmawSkyclaveRavager extends CardImpl { )); // When Grakmaw dies, create an X/X black and green Hydra creature token, where X is the number of +1/+1 counters on Grakmaw. - this.addAbility(new DiesTriggeredAbility(new GrakmawSkyclaveRavagerEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new GrakmawSkyclaveRavagerEffect())); } private GrakmawSkyclaveRavager(final GrakmawSkyclaveRavager card) { diff --git a/Mage.Sets/src/mage/cards/g/GuulDrazMucklord.java b/Mage.Sets/src/mage/cards/g/GuulDrazMucklord.java index aadcb6b714..c2846dbfca 100644 --- a/Mage.Sets/src/mage/cards/g/GuulDrazMucklord.java +++ b/Mage.Sets/src/mage/cards/g/GuulDrazMucklord.java @@ -2,7 +2,6 @@ package mage.cards.g; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; @@ -12,6 +11,7 @@ import mage.counters.CounterType; import mage.target.common.TargetControlledCreaturePermanent; import java.util.UUID; +import mage.abilities.common.DiesSourceTriggeredAbility; /** * @author TheElk801 @@ -26,7 +26,7 @@ public final class GuulDrazMucklord extends CardImpl { this.toughness = new MageInt(3); // When Guul Draz Mucklord dies, put a +1/+1 counter on target creature you control. - Ability ability = new DiesTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); + Ability ability = new DiesSourceTriggeredAbility(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); ability.addTarget(new TargetControlledCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/l/LeylineTyrant.java b/Mage.Sets/src/mage/cards/l/LeylineTyrant.java index c2cfee156c..e9605d006c 100644 --- a/Mage.Sets/src/mage/cards/l/LeylineTyrant.java +++ b/Mage.Sets/src/mage/cards/l/LeylineTyrant.java @@ -2,7 +2,6 @@ package mage.cards.l; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.Cost; @@ -19,6 +18,7 @@ import mage.players.Player; import mage.target.common.TargetAnyTarget; import java.util.UUID; +import mage.abilities.common.DiesSourceTriggeredAbility; /** * @author TheElk801 @@ -39,7 +39,7 @@ public final class LeylineTyrant extends CardImpl { this.addAbility(new SimpleStaticAbility(new LeylineTyrantManaEffect())); // When Leyline Tyrant dies, you may pay any amount of {R}. When you do, it deals that much damage to any target. - this.addAbility(new DiesTriggeredAbility(new LeylineTyrantDamageEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new LeylineTyrantDamageEffect())); } private LeylineTyrant(final LeylineTyrant card) { diff --git a/Mage.Sets/src/mage/cards/s/ShrivelingRot.java b/Mage.Sets/src/mage/cards/s/ShrivelingRot.java index 278fa9bab3..e64f2700c7 100644 --- a/Mage.Sets/src/mage/cards/s/ShrivelingRot.java +++ b/Mage.Sets/src/mage/cards/s/ShrivelingRot.java @@ -35,6 +35,7 @@ public final class ShrivelingRot extends CardImpl { // Choose one - // Until end of turn, whenever a creature is dealt damage, destroy it. this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new ShrivelingRotDestroyTriggeredAbility())); + // Until end of turn, whenever a creature dies, that creature's controller loses life equal to its toughness. Mode mode = new Mode(); mode.addEffect(new CreateDelayedTriggeredAbilityEffect(new ShrivelingRotLoseLifeTriggeredAbility())); diff --git a/Mage.Sets/src/mage/cards/t/TimeToFeed.java b/Mage.Sets/src/mage/cards/t/TimeToFeed.java index 9d5fc8bc15..cccd311504 100644 --- a/Mage.Sets/src/mage/cards/t/TimeToFeed.java +++ b/Mage.Sets/src/mage/cards/t/TimeToFeed.java @@ -102,8 +102,8 @@ class TimeToFeedTextEffect extends OneShotEffect { class TimeToFeedDiesTriggeredAbility extends DelayedTriggeredAbility { - private UUID watchedCreatureId; - private int zoneChangeCounter; + private final UUID watchedCreatureId; + private final int zoneChangeCounter; public TimeToFeedDiesTriggeredAbility(UUID watchedCreatureId, int zoneChangeCounter) { super(new GainLifeEffect(3), Duration.EndOfTurn, false); diff --git a/Mage.Sets/src/mage/cards/t/TroveWarden.java b/Mage.Sets/src/mage/cards/t/TroveWarden.java index 0472466244..1d417c96dc 100644 --- a/Mage.Sets/src/mage/cards/t/TroveWarden.java +++ b/Mage.Sets/src/mage/cards/t/TroveWarden.java @@ -2,7 +2,6 @@ package mage.cards.t; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.LandfallAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetForSourceEffect; @@ -20,6 +19,7 @@ import mage.target.common.TargetCardInYourGraveyard; import mage.util.CardUtil; import java.util.UUID; +import mage.abilities.common.DiesSourceTriggeredAbility; /** * @author TheElk801 @@ -50,7 +50,7 @@ public final class TroveWarden extends CardImpl { this.addAbility(ability); // When Trove Warden dies, put each permanent card exiled with it onto the battlefield under the control of that card's owner. - this.addAbility(new DiesTriggeredAbility(new TroveWardenEffect())); + this.addAbility(new DiesSourceTriggeredAbility(new TroveWardenEffect())); } private TroveWarden(final TroveWarden card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java index 6451651901..a3c7ccfce2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java @@ -1,537 +1,540 @@ -package org.mage.test.cards.abilities.keywords; - -import mage.cards.Card; -import mage.constants.EmptyNames; -import mage.constants.PhaseStep; -import mage.constants.Zone; -import mage.game.permanent.Permanent; -import org.junit.Assert; -import org.junit.Test; -import org.mage.test.serverside.base.CardTestPlayerBase; - -/** - * @author LevelX2 - */ -public class ManifestTest extends CardTestPlayerBase { - - /** - * Tests that ETB triggered abilities did not trigger for manifested cards - */ - @Test - public void testETBTriggeredAbilities() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); - // Manifest the top card of your library {1}{W} - addCard(Zone.HAND, playerA, "Soul Summons"); - - // Tranquil Cove enters the battlefield tapped. - // When Tranquil Cove enters the battlefield, you gain 1 life. - // {T}: Add {W} or {U}. - addCard(Zone.LIBRARY, playerA, "Tranquil Cove"); - skipInitShuffling(); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Summons"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - // a facedown creature is on the battlefield - assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); - assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); - // not tapped - assertTapped(EmptyNames.FACE_DOWN_CREATURE.toString(), false); - } - - /** - * If Doomwake Giant gets manifested, it's Constellation trigger may not - * trigger - */ - @Test - public void testETBTriggeredAbilities2() { - addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); - // Manifest the top card of your library {1}{W} - addCard(Zone.HAND, playerA, "Soul Summons"); - - // Constellation - When Doomwake Giant or another enchantment enters the battlefield - // under your control, creatures your opponents control get -1/-1 until end of turn. - addCard(Zone.LIBRARY, playerA, "Doomwake Giant"); - - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); - skipInitShuffling(); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Summons"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - // a facedown creature is on the battlefield - assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); - assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); - // PlayerB's Silvercoat Lion should not have get -1/-1/ - assertPermanentCount(playerB, "Silvercoat Lion", 1); - assertPowerToughness(playerB, "Silvercoat Lion", 2, 2); - } - - /** - * If Doomwake Giant gets manifested, it's Constellation trigger may not - * trigger - */ - @Test - public void testETBTriggeredAbilities3() { - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - // Exile target creature. Its controller manifests the top card of their library {1}{U} - addCard(Zone.HAND, playerB, "Reality Shift"); - - // Constellation - When Doomwake Giant or another enchantment enters the battlefield - // under your control, creatures your opponents control get -1/-1 until end of turn. - addCard(Zone.LIBRARY, playerA, "Doomwake Giant"); - - addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); - - skipInitShuffling(); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Reality Shift", "Silvercoat Lion"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - assertGraveyardCount(playerB, "Reality Shift", 1); - assertExileCount("Silvercoat Lion", 1); - // a facedown creature is on the battlefield - assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); - assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); - // PlayerA's Pillarfield Ox should not have get -1/-1/ - assertPermanentCount(playerB, "Pillarfield Ox", 1); - assertPowerToughness(playerB, "Pillarfield Ox", 2, 4); - } - - /** - * If Doomwake Giant gets manifested, it's Constellation trigger may not - * trigger - */ - @Test - public void testNylea() { - setStrictChooseMode(true); - - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - // Exile target creature. Its controller manifests the top card of their library {1}{U} - addCard(Zone.HAND, playerB, "Reality Shift"); - - // As long as your devotion to white is less than five, Nylea isn't a creature. - // (Each {G} in the mana costs of permanents you control counts towards your devotion to green.) - addCard(Zone.LIBRARY, playerA, "Nylea, God of the Hunt"); - - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); - - skipInitShuffling(); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Reality Shift", "Silvercoat Lion"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - assertGraveyardCount(playerB, "Reality Shift", 1); - assertExileCount("Silvercoat Lion", 1); - // a facedown creature is on the battlefield - assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); - assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); - - } - - /* - Had a Foundry Street Denizen and another creature out. - Opponent Reality Shift'ed the other creature, manifested card was a red creature. This pumped the foundry street denizen even though it shouldn't. - */ - @Test - public void testColorOfManifestedCardDoesNotCount() { - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - // Exile target creature. Its controller manifests the top card of their library {1}{U} - addCard(Zone.HAND, playerB, "Reality Shift"); - - // Gore Swine {2}{R} - // 4/1 - addCard(Zone.LIBRARY, playerA, "Gore Swine"); - - // Whenever another red creature enters the battlefield under your control, Foundry Street Denizen gets +1/+0 until end of turn. - addCard(Zone.BATTLEFIELD, playerA, "Foundry Street Denizen"); - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); - - skipInitShuffling(); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Reality Shift", "Silvercoat Lion"); - - setStopAt(1, PhaseStep.BEGIN_COMBAT); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - assertGraveyardCount(playerB, "Reality Shift", 1); - assertExileCount("Silvercoat Lion", 1); - // a facedown creature is on the battlefield - assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); - assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); - assertPowerToughness(playerA, "Foundry Street Denizen", 1, 1); - - } - - /* - I casted a Silence the Believers on a manifested card. It moved to the exile zone face-down. - */ - @Test - public void testCardGetsExiledFaceUp() { - addCard(Zone.BATTLEFIELD, playerB, "Island", 2); - addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4); - // Exile target creature. Its controller manifests the top card of their library {1}{U} - addCard(Zone.HAND, playerB, "Reality Shift"); - // Silence the Believers - Instant {2}{B}{B} - // Strive — Silence the Believers costs more to cast for each target beyond the first. - // Exile any number of target creatures and all Auras attached to them. - addCard(Zone.HAND, playerB, "Silence the Believers"); - // Gore Swine {2}{R} - // 4/1 - addCard(Zone.LIBRARY, playerA, "Gore Swine"); - - // Whenever another red creature enters the battlefield under your control, Foundry Street Denizen gets +1/+0 until end of turn. - addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); - - skipInitShuffling(); - - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Reality Shift", "Silvercoat Lion"); - // showBattlefield("A battle", 1, PhaseStep.POSTCOMBAT_MAIN, playerA); - // showBattlefield("B battle", 1, PhaseStep.POSTCOMBAT_MAIN, playerB); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Silence the Believers", EmptyNames.FACE_DOWN_CREATURE.toString()); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - assertGraveyardCount(playerB, "Reality Shift", 1); - assertExileCount("Silvercoat Lion", 1); - assertExileCount("Gore Swine", 1); - // no facedown creature is on the battlefield - assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0); - - for (Card card : currentGame.getExile().getAllCards(currentGame)) { - if (card.getName().equals("Gore Swine")) { - Assert.assertTrue("Gore Swine may not be face down in exile", !card.isFaceDown(currentGame)); - } - } - - } - - // Qarsi High Priest went to manifest Illusory Gains, - // but it made me choose a target for gains, then enchanted the card to that creature. - @Test - public void testManifestAura() { - - addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); - // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. - addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - - addCard(Zone.LIBRARY, playerB, "Illusory Gains", 1); - addCard(Zone.LIBRARY, playerB, "Mountain", 1); - - skipInitShuffling(); - - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); - setChoice(playerB, "Silvercoat Lion"); - - setStopAt(2, PhaseStep.BEGIN_COMBAT); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - - assertGraveyardCount(playerB, "Illusory Gains", 0); - assertGraveyardCount(playerB, "Silvercoat Lion", 1); - - // a facedown creature is on the battlefield - assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); - - } - - // Check if a Megamorph card is manifested and turned face up by their megamorph ability - // it gets the +1/+1 counter. - // 701.33c - // If a card with morph is manifested, its controller may turn that card face up using - // either the procedure described in rule 702.36e to turn a face-down permanent with morph face up - // or the procedure described above to turn a manifested permanent face up. - @Test - public void testManifestMegamorph_TurnUpByMegamorphCost() { - addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); - addCard(Zone.BATTLEFIELD, playerB, "Forest", 6); - // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. - addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - - // Reach (This creature can block creatures with flying.) - // Megamorph {5}{G} - addCard(Zone.LIBRARY, playerB, "Aerie Bowmasters", 1); - addCard(Zone.LIBRARY, playerB, "Mountain", 1); - - skipInitShuffling(); - - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); - setChoice(playerB, "Silvercoat Lion"); - - activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{5}{G}: Turn"); - - setStrictChooseMode(true); - setStopAt(2, PhaseStep.END_TURN); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - - assertGraveyardCount(playerB, "Silvercoat Lion", 1); - - assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0); - assertPermanentCount(playerB, "Aerie Bowmasters", 1); - assertPowerToughness(playerB, "Aerie Bowmasters", 4, 5); // 3/4 and the +1/+1 counter from Megamorph - Permanent aerie = getPermanent("Aerie Bowmasters", playerB); - Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen()); - } - - @Test - public void testManifestMegamorph_TurnUpBySimpleCost() { - addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); - addCard(Zone.BATTLEFIELD, playerB, "Forest", 4); - // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. - addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - - // {2}{G}{G} - // Reach (This creature can block creatures with flying.) - // Megamorph {5}{G} - addCard(Zone.LIBRARY, playerB, "Aerie Bowmasters", 1); - addCard(Zone.LIBRARY, playerB, "Mountain", 1); - - skipInitShuffling(); - - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); - setChoice(playerB, "Silvercoat Lion"); - - activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{2}{G}{G}: Turn"); - - setStrictChooseMode(true); - setStopAt(2, PhaseStep.END_TURN); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - - assertGraveyardCount(playerB, "Silvercoat Lion", 1); - - assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0); - assertPermanentCount(playerB, "Aerie Bowmasters", 1); - assertPowerToughness(playerB, "Aerie Bowmasters", 3, 4); // 3/4 without counter (megamorph not used) - Permanent aerie = getPermanent("Aerie Bowmasters", playerB); - Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen()); - } - - /** - * When a Forest came manifested into play my Courser of Kruphix gained me a - * life. - */ - @Test - public void testManifestForest() { - - addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); - // Play with the top card of your library revealed. - // You may play the top card of your library if it's a land card. - // Whenever a land enters the battlefield under your control, you gain 1 life. - addCard(Zone.BATTLEFIELD, playerB, "Courser of Kruphix", 1); - - // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. - addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - - addCard(Zone.LIBRARY, playerB, "Forest", 1); - - skipInitShuffling(); - - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); - setChoice(playerB, "Silvercoat Lion"); - - setStopAt(2, PhaseStep.END_TURN); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - - assertGraveyardCount(playerB, "Silvercoat Lion", 1); - - assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); - - } - - /** - * Whisperwood Elemental - Its sacrifice ability doesn't work.. - */ - @Test - public void testWhisperwoodElemental() { - addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); - // Seismic Rupture deals 2 damage to each creature without flying. - addCard(Zone.HAND, playerA, "Seismic Rupture", 1); - - // At the beginning of your end step, manifest the top card of your library. - // Sacrifice Whisperwood Elemental: Until end of turn, face-up, nontoken creatures you control gain "When this creature dies, manifest the top card of your library." - addCard(Zone.BATTLEFIELD, playerB, "Whisperwood Elemental", 1); - - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 2); - - activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Sacrifice"); - - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Seismic Rupture"); - - setStopAt(1, PhaseStep.END_TURN); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - - assertGraveyardCount(playerA, "Seismic Rupture", 1); - assertGraveyardCount(playerB, "Whisperwood Elemental", 1); - assertGraveyardCount(playerB, "Silvercoat Lion", 2); - - assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 2); - - } - - /** - * I sacrificed a manifested face-down Smothering Abomination to Nantuko - * Husk and it made me draw a card. - */ - @Test - public void testDiesTriggeredAbilitiesOfManifestedCreatures() { - - addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); - - // Sacrifice a creature: Nantuko Husk gets +2/+2 until end of turn. - addCard(Zone.BATTLEFIELD, playerB, "Nantuko Husk", 1); - - // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. - addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - - // Devoid - // Flying - // At the beginning of your upkeep, sacrifice a creature - // Whenever you sacrifice a creature, draw a card. - addCard(Zone.LIBRARY, playerB, "Mountain", 1); - addCard(Zone.LIBRARY, playerB, "Smothering Abomination", 1); - addCard(Zone.LIBRARY, playerB, "Mountain", 1); - - skipInitShuffling(); - - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); - setChoice(playerB, "Silvercoat Lion"); - - activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Sacrifice a creature"); - setChoice(playerB, EmptyNames.FACE_DOWN_CREATURE.toString()); - - setStrictChooseMode(true); - setStopAt(2, PhaseStep.END_TURN); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - - assertPermanentCount(playerB, "Qarsi High Priest", 1); - assertPermanentCount(playerB, "Nantuko Husk", 1); - - assertGraveyardCount(playerB, "Silvercoat Lion", 1); - assertGraveyardCount(playerB, "Smothering Abomination", 1); - - assertPowerToughness(playerB, "Nantuko Husk", 4, 4); - - assertHandCount(playerB, "Mountain", 1); - - } - - @Test - public void test_ManifestSorceryAndBlinkIt() { - - addCard(Zone.BATTLEFIELD, playerB, "Swamp", 1); - addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); - - // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. - addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); - addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); - - // Exile target creature you control, then return that card to the battlefield under your control. - addCard(Zone.HAND, playerB, "Cloudshift", 1); //Instant {W} - - - // Devoid - // Flying - // At the beginning of your upkeep, sacrifice a creature - // Whenever you sacrifice a creature, draw a card. - addCard(Zone.LIBRARY, playerB, "Mountain", 1); - addCard(Zone.LIBRARY, playerB, "Lightning Bolt", 1); - addCard(Zone.LIBRARY, playerB, "Mountain", 1); - - skipInitShuffling(); - - activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); - setChoice(playerB, "Silvercoat Lion"); - - waitStackResolved(2, PhaseStep.PRECOMBAT_MAIN, playerB); - - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cloudshift", EmptyNames.FACE_DOWN_CREATURE.toString()); - - setStrictChooseMode(true); - setStopAt(2, PhaseStep.END_TURN); - execute(); - assertAllCommandsUsed(); - - // no life gain - assertLife(playerA, 20); - assertLife(playerB, 20); - - assertPermanentCount(playerB, "Qarsi High Priest", 1); - - assertGraveyardCount(playerB, "Silvercoat Lion", 1); - assertGraveyardCount(playerB, "Cloudshift", 1); - - assertPermanentCount(playerB, "Lightning Bolt", 0); - assertExileCount(playerB, "Lightning Bolt", 1); - - assertHandCount(playerB, "Mountain", 1); - - } -} +package org.mage.test.cards.abilities.keywords; + +import mage.cards.Card; +import mage.constants.EmptyNames; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author LevelX2 + */ +public class ManifestTest extends CardTestPlayerBase { + + /** + * Tests that ETB triggered abilities did not trigger for manifested cards + */ + @Test + public void testETBTriggeredAbilities() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // Manifest the top card of your library {1}{W} + addCard(Zone.HAND, playerA, "Soul Summons"); + + // Tranquil Cove enters the battlefield tapped. + // When Tranquil Cove enters the battlefield, you gain 1 life. + // {T}: Add {W} or {U}. + addCard(Zone.LIBRARY, playerA, "Tranquil Cove"); + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Summons"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + // a facedown creature is on the battlefield + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); + // not tapped + assertTapped(EmptyNames.FACE_DOWN_CREATURE.toString(), false); + } + + /** + * If Doomwake Giant gets manifested, it's Constellation trigger may not + * trigger + */ + @Test + public void testETBTriggeredAbilities2() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + // Manifest the top card of your library {1}{W} + addCard(Zone.HAND, playerA, "Soul Summons"); + + // Constellation - When Doomwake Giant or another enchantment enters the battlefield + // under your control, creatures your opponents control get -1/-1 until end of turn. + addCard(Zone.LIBRARY, playerA, "Doomwake Giant"); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Summons"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + // a facedown creature is on the battlefield + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); + // PlayerB's Silvercoat Lion should not have get -1/-1/ + assertPermanentCount(playerB, "Silvercoat Lion", 1); + assertPowerToughness(playerB, "Silvercoat Lion", 2, 2); + } + + /** + * If Doomwake Giant gets manifested, it's Constellation trigger may not + * trigger + */ + @Test + public void testETBTriggeredAbilities3() { + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + // Exile target creature. Its controller manifests the top card of their library {1}{U} + addCard(Zone.HAND, playerB, "Reality Shift"); + + // Constellation - When Doomwake Giant or another enchantment enters the battlefield + // under your control, creatures your opponents control get -1/-1 until end of turn. + addCard(Zone.LIBRARY, playerA, "Doomwake Giant"); + + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Reality Shift", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + assertGraveyardCount(playerB, "Reality Shift", 1); + assertExileCount("Silvercoat Lion", 1); + // a facedown creature is on the battlefield + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); + // PlayerA's Pillarfield Ox should not have get -1/-1/ + assertPermanentCount(playerB, "Pillarfield Ox", 1); + assertPowerToughness(playerB, "Pillarfield Ox", 2, 4); + } + + /** + * If Doomwake Giant gets manifested, it's Constellation trigger may not + * trigger + */ + @Test + public void testNylea() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + // Exile target creature. Its controller manifests the top card of their library {1}{U} + addCard(Zone.HAND, playerB, "Reality Shift"); + + // As long as your devotion to white is less than five, Nylea isn't a creature. + // (Each {G} in the mana costs of permanents you control counts towards your devotion to green.) + addCard(Zone.LIBRARY, playerA, "Nylea, God of the Hunt"); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Reality Shift", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + assertGraveyardCount(playerB, "Reality Shift", 1); + assertExileCount("Silvercoat Lion", 1); + // a facedown creature is on the battlefield + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); + + } + + /* + Had a Foundry Street Denizen and another creature out. + Opponent Reality Shift'ed the other creature, manifested card was a red creature. This pumped the foundry street denizen even though it shouldn't. + */ + @Test + public void testColorOfManifestedCardDoesNotCount() { + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + // Exile target creature. Its controller manifests the top card of their library {1}{U} + addCard(Zone.HAND, playerB, "Reality Shift"); + + // Gore Swine {2}{R} + // 4/1 + addCard(Zone.LIBRARY, playerA, "Gore Swine"); + + // Whenever another red creature enters the battlefield under your control, Foundry Street Denizen gets +1/+0 until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Foundry Street Denizen"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Reality Shift", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + assertGraveyardCount(playerB, "Reality Shift", 1); + assertExileCount("Silvercoat Lion", 1); + // a facedown creature is on the battlefield + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); + assertPowerToughness(playerA, "Foundry Street Denizen", 1, 1); + + } + + /* + I casted a Silence the Believers on a manifested card. It moved to the exile zone face-down. + */ + @Test + public void testCardGetsExiledFaceUp() { + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4); + // Exile target creature. Its controller manifests the top card of their library {1}{U} + addCard(Zone.HAND, playerB, "Reality Shift"); + // Silence the Believers - Instant {2}{B}{B} + // Strive — Silence the Believers costs more to cast for each target beyond the first. + // Exile any number of target creatures and all Auras attached to them. + addCard(Zone.HAND, playerB, "Silence the Believers"); + // Gore Swine {2}{R} + // 4/1 + addCard(Zone.LIBRARY, playerA, "Gore Swine"); + + // Whenever another red creature enters the battlefield under your control, Foundry Street Denizen gets +1/+0 until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Reality Shift", "Silvercoat Lion"); + // showBattlefield("A battle", 1, PhaseStep.POSTCOMBAT_MAIN, playerA); + // showBattlefield("B battle", 1, PhaseStep.POSTCOMBAT_MAIN, playerB); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Silence the Believers", EmptyNames.FACE_DOWN_CREATURE.toString()); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + assertGraveyardCount(playerB, "Reality Shift", 1); + assertExileCount("Silvercoat Lion", 1); + assertExileCount("Gore Swine", 1); + // no facedown creature is on the battlefield + assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0); + + for (Card card : currentGame.getExile().getAllCards(currentGame)) { + if (card.getName().equals("Gore Swine")) { + Assert.assertTrue("Gore Swine may not be face down in exile", !card.isFaceDown(currentGame)); + } + } + + } + + // Qarsi High Priest went to manifest Illusory Gains, + // but it made me choose a target for gains, then enchanted the card to that creature. + @Test + public void testManifestAura() { + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. + addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + addCard(Zone.LIBRARY, playerB, "Illusory Gains", 1); + addCard(Zone.LIBRARY, playerB, "Mountain", 1); + + skipInitShuffling(); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); + setChoice(playerB, "Silvercoat Lion"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerB, "Illusory Gains", 0); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + // a facedown creature is on the battlefield + assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + + } + + // Check if a Megamorph card is manifested and turned face up by their megamorph ability + // it gets the +1/+1 counter. + // 701.33c + // If a card with morph is manifested, its controller may turn that card face up using + // either the procedure described in rule 702.36e to turn a face-down permanent with morph face up + // or the procedure described above to turn a manifested permanent face up. + @Test + public void testManifestMegamorph_TurnUpByMegamorphCost() { + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 6); + // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. + addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + // Reach (This creature can block creatures with flying.) + // Megamorph {5}{G} + addCard(Zone.LIBRARY, playerB, "Aerie Bowmasters", 1); + addCard(Zone.LIBRARY, playerB, "Mountain", 1); + + skipInitShuffling(); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); + setChoice(playerB, "Silvercoat Lion"); + + activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{5}{G}: Turn"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0); + assertPermanentCount(playerB, "Aerie Bowmasters", 1); + assertPowerToughness(playerB, "Aerie Bowmasters", 4, 5); // 3/4 and the +1/+1 counter from Megamorph + Permanent aerie = getPermanent("Aerie Bowmasters", playerB); + Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen()); + } + + @Test + public void testManifestMegamorph_TurnUpBySimpleCost() { + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 4); + // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. + addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + // {2}{G}{G} + // Reach (This creature can block creatures with flying.) + // Megamorph {5}{G} + addCard(Zone.LIBRARY, playerB, "Aerie Bowmasters", 1); + addCard(Zone.LIBRARY, playerB, "Mountain", 1); + + skipInitShuffling(); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); + setChoice(playerB, "Silvercoat Lion"); + + activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "{2}{G}{G}: Turn"); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0); + assertPermanentCount(playerB, "Aerie Bowmasters", 1); + assertPowerToughness(playerB, "Aerie Bowmasters", 3, 4); // 3/4 without counter (megamorph not used) + Permanent aerie = getPermanent("Aerie Bowmasters", playerB); + Assert.assertTrue("Aerie Bowmasters has to be green", aerie != null && aerie.getColor(currentGame).isGreen()); + } + + /** + * When a Forest came manifested into play my Courser of Kruphix gained me a + * life. + */ + @Test + public void testManifestForest() { + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + // Play with the top card of your library revealed. + // You may play the top card of your library if it's a land card. + // Whenever a land enters the battlefield under your control, you gain 1 life. + addCard(Zone.BATTLEFIELD, playerB, "Courser of Kruphix", 1); + + // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. + addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + addCard(Zone.LIBRARY, playerB, "Forest", 1); + + skipInitShuffling(); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); + setChoice(playerB, "Silvercoat Lion"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + + assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1); + + } + + /** + * Whisperwood Elemental - Its sacrifice ability doesn't work.. + */ + @Test + public void testWhisperwoodElemental() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Seismic Rupture deals 2 damage to each creature without flying. + addCard(Zone.HAND, playerA, "Seismic Rupture", 1); + + // At the beginning of your end step, manifest the top card of your library. + // Sacrifice Whisperwood Elemental: Until end of turn, face-up, nontoken creatures you control gain "When this creature dies, manifest the top card of your library." + addCard(Zone.BATTLEFIELD, playerB, "Whisperwood Elemental", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 2); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Sacrifice"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Seismic Rupture"); + setChoice(playerB, "When {this} dies"); // Order of triggers + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Seismic Rupture", 1); + assertGraveyardCount(playerB, "Whisperwood Elemental", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 2); + + assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 2); + + } + + /** + * I sacrificed a manifested face-down Smothering Abomination to Nantuko + * Husk and it made me draw a card. + */ + @Test + public void testDiesTriggeredAbilitiesOfManifestedCreatures() { + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + + // Sacrifice a creature: Nantuko Husk gets +2/+2 until end of turn. + addCard(Zone.BATTLEFIELD, playerB, "Nantuko Husk", 1); + + // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. + addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + // Devoid + // Flying + // At the beginning of your upkeep, sacrifice a creature + // Whenever you sacrifice a creature, draw a card. + addCard(Zone.LIBRARY, playerB, "Mountain", 1); + addCard(Zone.LIBRARY, playerB, "Smothering Abomination", 1); + addCard(Zone.LIBRARY, playerB, "Mountain", 1); + + skipInitShuffling(); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); + setChoice(playerB, "Silvercoat Lion"); + + activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Sacrifice a creature"); + setChoice(playerB, EmptyNames.FACE_DOWN_CREATURE.toString()); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerB, "Qarsi High Priest", 1); + assertPermanentCount(playerB, "Nantuko Husk", 1); + + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + assertGraveyardCount(playerB, "Smothering Abomination", 1); + + assertPowerToughness(playerB, "Nantuko Husk", 4, 4); + + assertHandCount(playerB, "Mountain", 1); + + } + + @Test + public void test_ManifestSorceryAndBlinkIt() { + + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + + // {1}{B}, {T}, Sacrifice another creature: Manifest the top card of your library. + addCard(Zone.BATTLEFIELD, playerB, "Qarsi High Priest", 1); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + // Exile target creature you control, then return that card to the battlefield under your control. + addCard(Zone.HAND, playerB, "Cloudshift", 1); //Instant {W} + + + // Devoid + // Flying + // At the beginning of your upkeep, sacrifice a creature + // Whenever you sacrifice a creature, draw a card. + addCard(Zone.LIBRARY, playerB, "Mountain", 1); + addCard(Zone.LIBRARY, playerB, "Lightning Bolt", 1); + addCard(Zone.LIBRARY, playerB, "Mountain", 1); + + skipInitShuffling(); + + activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{1}{B}, {T}, Sacrifice another creature"); + setChoice(playerB, "Silvercoat Lion"); + + waitStackResolved(2, PhaseStep.PRECOMBAT_MAIN, playerB); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cloudshift", EmptyNames.FACE_DOWN_CREATURE.toString()); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerB, "Qarsi High Priest", 1); + + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + assertGraveyardCount(playerB, "Cloudshift", 1); + + assertPermanentCount(playerB, "Lightning Bolt", 0); + assertExileCount(playerB, "Lightning Bolt", 1); + + assertHandCount(playerB, "Mountain", 1); + + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/facedown/GrimHaruspexTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/facedown/GrimHaruspexTest.java index d794aaba88..deea5f4a59 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/facedown/GrimHaruspexTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/facedown/GrimHaruspexTest.java @@ -8,7 +8,11 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class GrimHaruspexTest extends CardTestPlayerBase { @Test public void testMorphed() { + setStrictChooseMode(true); + addCard(Zone.HAND, playerA, "Wrath of God"); + // Morph {B} + // Whenever another nontoken creature you control dies, draw a card. addCard(Zone.HAND, playerA, "Grim Haruspex"); addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); @@ -19,6 +23,8 @@ public class GrimHaruspexTest extends CardTestPlayerBase { setStopAt(1, PhaseStep.END_COMBAT); execute(); + assertAllCommandsUsed(); + assertGraveyardCount(playerA, "Grim Haruspex", 1); assertHandCount(playerA, 0); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/facedown/TriggerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/facedown/TriggerTest.java new file mode 100644 index 0000000000..54035a4cfe --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/facedown/TriggerTest.java @@ -0,0 +1,62 @@ + +package org.mage.test.cards.facedown; + +import mage.cards.Card; +import mage.constants.EmptyNames; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + + + +public class TriggerTest extends CardTestPlayerBase { + + /** + * Midnight Reaper triggers when dies face down #7063 + * Ixidron has turned Midnight Reaper and Balduvian Bears face down: + * + */ + + // test that cards imprinted using Summoner's Egg are face down + @Test + public void testReaperDoesNotTriggerDiesTriggerFaceDown() { + setStrictChooseMode(true); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + // As Ixidron enters the battlefield, turn all other nontoken creatures face down. + // Ixidron's power and toughness are each equal to the number of face-down creatures on the battlefield. + addCard(Zone.HAND, playerA, "Ixidron"); // Creature {3}{U}{U} (*/*) + // Whenever a nontoken creature you control dies, Midnight Reaper deals 1 damage to you and you draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Midnight Reaper"); // Creature {2}{B} + + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.HAND, playerB, "Lightning Bolt"); // Instant 3 damage + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ixidron"); + + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", EmptyNames.FACE_DOWN_CREATURE.toString()); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertAllCommandsUsed(); + + assertGraveyardCount(playerB, "Lightning Bolt", 1); + + assertGraveyardCount(playerA, "Midnight Reaper", 1); + assertGraveyardCount(playerA, "Ixidron", 1); + + assertHandCount(playerA, 0); + assertLife(playerA, 20); + + } +} \ No newline at end of file diff --git a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java index fb9f834c5a..08bc28b173 100644 --- a/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/TriggeredAbilityImpl.java @@ -13,6 +13,8 @@ import mage.util.CardUtil; import java.util.Locale; import java.util.UUID; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; /** * @author BetaSteward_at_googlemail.com @@ -222,4 +224,37 @@ public abstract class TriggeredAbilityImpl extends AbilityImpl implements Trigge return optional; } + public static boolean isInUseableZoneDiesTrigger(TriggeredAbility source, GameEvent event, Game game) { + // Get the source permanent of the ability + MageObject sourceObject = null; + if (game.getState().getZone(source.getSourceId()) == Zone.BATTLEFIELD) { + sourceObject = game.getPermanent(source.getSourceId()); + } else { + if (game.getShortLivingLKI(source.getSourceId(), Zone.BATTLEFIELD)) { + sourceObject = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); + } + } + if (sourceObject == null) { // source is no permanent + sourceObject = game.getObject(source.getSourceId()); + if (sourceObject == null || sourceObject.isPermanent()) { + return false; // No source object found => ability is not valid + } + } + + if (!source.hasSourceObjectAbility(game, sourceObject, event)) { + return false; // the permanent does currently not have or before it dies the ability so no trigger + } + + // check now it is in graveyard (only if it is no token and was the target itself) + if (source.getSourceId().equals(event.getTargetId()) // source is also the target + && !(sourceObject instanceof PermanentToken) // it's no token + && sourceObject.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(source.getSourceId())) { // It's in the next zone + Zone after = game.getState().getZone(source.getSourceId()); + if (after == null || !Zone.GRAVEYARD.match(after)) { // Zone is not the graveyard + return false; // Moving to graveyard was replaced so no trigger + } + } + + return true; + } } diff --git a/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java index 1928b30063..7603da5823 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesCreatureTriggeredAbility.java @@ -1,5 +1,6 @@ package mage.abilities.common; +import mage.MageObject; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; @@ -81,6 +82,11 @@ public class DiesCreatureTriggeredAbility extends TriggeredAbilityImpl { return false; } + @Override + public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { + return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game); + } + @Override public String getRule() { return "Whenever " + filter.getMessage() + " dies, " + super.getRule(); diff --git a/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java index 9a9d219669..906c577625 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesSourceTriggeredAbility.java @@ -1,13 +1,12 @@ package mage.abilities.common; import mage.MageObject; +import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentToken; /** * @author BetaSteward_at_googlemail.com @@ -26,48 +25,15 @@ public class DiesSourceTriggeredAbility extends ZoneChangeTriggeredAbility { super(ability); } - @Override - public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { - // check it was previously on battlefield - Permanent before = ((ZoneChangeEvent) event).getTarget(); - if (before == null) { - return false; - } - if (!this.hasSourceObjectAbility(game, before, event)) { // the permanent does not have the ability so no trigger - return false; - } - // check now it is in graveyard if it is no token - if (!(before instanceof PermanentToken) && before.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(sourceId)) { - Zone after = game.getState().getZone(sourceId); - return after != null && Zone.GRAVEYARD.match(after); - } else { - // Already moved to another zone, so guess it's ok - return true; - } - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - if (super.checkEventType(event, game)) { - return ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD; - } - return false; - } - @Override public DiesSourceTriggeredAbility copy() { return new DiesSourceTriggeredAbility(this); } - + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (super.checkTrigger(event, game)) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getTarget().isTransformable()) { - if (!zEvent.getTarget().getAbilities().contains(this)) { - return false; - } - } + ZoneChangeEvent zEvent = (ZoneChangeEvent) event; + if (zEvent.isDiesEvent() && event.getTargetId().equals(getSourceId())) { for (Effect effect : getEffects()) { effect.setValue("permanentLeftBattlefield", zEvent.getTarget()); } @@ -75,5 +41,12 @@ public class DiesSourceTriggeredAbility extends ZoneChangeTriggeredAbility { } return false; } + + @Override + public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { + return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game); + } + + } diff --git a/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureOrPlaneswalkerTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureOrPlaneswalkerTriggeredAbility.java index 6d43388909..53f24bccb4 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureOrPlaneswalkerTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureOrPlaneswalkerTriggeredAbility.java @@ -41,31 +41,15 @@ public class DiesThisOrAnotherCreatureOrPlaneswalkerTriggeredAbility extends Tri public boolean checkEventType(GameEvent event, Game game) { return event.getType() == GameEvent.EventType.ZONE_CHANGE; } - - @Override - public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { - Permanent sourcePermanent = null; - if (game.getState().getZone(getSourceId()) == Zone.BATTLEFIELD) { - sourcePermanent = game.getPermanent(getSourceId()); - } else { - if (game.getShortLivingLKI(getSourceId(), Zone.BATTLEFIELD)) { - sourcePermanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD); - } - } - if (sourcePermanent == null) { - return false; - } - return hasSourceObjectAbility(game, sourcePermanent, event); - } - + @Override public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; +// if (game.getPermanentOrLKIBattlefield(getSourceId()) == null) { +// return false; +// } - if (game.getPermanentOrLKIBattlefield(getSourceId()) == null) { - return false; - } - +// if (zEvent.isDiesEvent()) { if (zEvent.getTarget() != null) { if (zEvent.getTarget().getId().equals(this.getSourceId())) { @@ -80,6 +64,23 @@ public class DiesThisOrAnotherCreatureOrPlaneswalkerTriggeredAbility extends Tri return false; } + @Override + public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { + return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game); +// Permanent sourcePermanent = null; +// if (game.getState().getZone(getSourceId()) == Zone.BATTLEFIELD) { +// sourcePermanent = game.getPermanent(getSourceId()); +// } else { +// if (game.getShortLivingLKI(getSourceId(), Zone.BATTLEFIELD)) { +// sourcePermanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD); +// } +// } +// if (sourcePermanent == null) { +// return false; +// } +// return hasSourceObjectAbility(game, sourcePermanent, event); + } + @Override public String getRule() { return "Whenever {this} or another " + filter.getMessage() + " dies, " + super.getRule(); diff --git a/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureTriggeredAbility.java index 61478cf9ba..7fb6d9611c 100644 --- a/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/DiesThisOrAnotherCreatureTriggeredAbility.java @@ -48,30 +48,14 @@ public class DiesThisOrAnotherCreatureTriggeredAbility extends TriggeredAbilityI return event.getType() == GameEvent.EventType.ZONE_CHANGE; } - @Override - public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { - Permanent sourcePermanent = null; - if (game.getState().getZone(getSourceId()) == Zone.BATTLEFIELD) { - sourcePermanent = game.getPermanent(getSourceId()); - } else { - if (game.getShortLivingLKI(getSourceId(), Zone.BATTLEFIELD)) { - sourcePermanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD); - } - } - if (sourcePermanent == null) { - return false; - } - return hasSourceObjectAbility(game, sourcePermanent, event); - } - @Override public boolean checkTrigger(GameEvent event, Game game) { ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - - if (game.getPermanentOrLKIBattlefield(getSourceId()) == null) { - return false; - } - +// +// if (game.getPermanentOrLKIBattlefield(getSourceId()) == null) { +// return false; +// } +// if (zEvent.isDiesEvent()) { if (zEvent.getTarget() != null) { if (!applyFilterOnSource && zEvent.getTarget().getId().equals(this.getSourceId())) { @@ -85,6 +69,24 @@ public class DiesThisOrAnotherCreatureTriggeredAbility extends TriggeredAbilityI } return false; } + + @Override + public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { + return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, event, game); +// +// Permanent sourcePermanent = null; +// if (game.getState().getZone(getSourceId()) == Zone.BATTLEFIELD) { +// sourcePermanent = game.getPermanent(getSourceId()); +// } else { +// if (game.getShortLivingLKI(getSourceId(), Zone.BATTLEFIELD)) { +// sourcePermanent = (Permanent) game.getLastKnownInformation(getSourceId(), Zone.BATTLEFIELD); +// } +// } +// if (sourcePermanent == null) { +// return false; +// } +// return hasSourceObjectAbility(game, sourcePermanent, event); + } @Override public String getRule() { diff --git a/Mage/src/main/java/mage/abilities/common/DiesTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/DiesTriggeredAbility.java deleted file mode 100644 index 8d82bd7d77..0000000000 --- a/Mage/src/main/java/mage/abilities/common/DiesTriggeredAbility.java +++ /dev/null @@ -1,79 +0,0 @@ -package mage.abilities.common; - -import mage.MageObject; -import mage.abilities.effects.Effect; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; -import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentToken; - -/** - * @author BetaSteward_at_googlemail.com - */ -public class DiesTriggeredAbility extends ZoneChangeTriggeredAbility { - - public DiesTriggeredAbility(Effect effect, boolean optional) { - super(Zone.BATTLEFIELD, Zone.GRAVEYARD, effect, "When {this} dies, ", optional); - } - - public DiesTriggeredAbility(Effect effect) { - this(effect, false); - } - - public DiesTriggeredAbility(DiesTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean isInUseableZone(Game game, MageObject source, GameEvent event) { - // check it was previously on battlefield - Permanent before = ((ZoneChangeEvent) event).getTarget(); - if (before == null) { - return false; - } - if (!this.hasSourceObjectAbility(game, before, event)) { // the permanent does not have the ability so no trigger - return false; - } - // check now it is in graveyard if it is no token - if (!(before instanceof PermanentToken) && before.getZoneChangeCounter(game) + 1 == game.getState().getZoneChangeCounter(sourceId)) { - Zone after = game.getState().getZone(sourceId); - return after != null && Zone.GRAVEYARD.match(after); - } else { - // Already moved to another zone, so guess it's ok - return true; - } - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - if (super.checkEventType(event, game)) { - return ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD && ((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD; - } - return false; - } - - @Override - public DiesTriggeredAbility copy() { - return new DiesTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (super.checkTrigger(event, game)) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (zEvent.getTarget().isTransformable()) { - if (!zEvent.getTarget().getAbilities().contains(this)) { - return false; - } - } - for (Effect effect : getEffects()) { - effect.setValue("permanentLeftBattlefield", zEvent.getTarget()); - } - return true; - } - return false; - } - -}