diff --git a/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java b/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java index 74de65b44f7..ebb28bb5b93 100644 --- a/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java +++ b/Mage.Sets/src/mage/cards/f/FeatherTheRedeemed.java @@ -167,11 +167,9 @@ class FeatherTheRedeemedEffect extends ReplacementEffectImpl { if (zEvent.getFromZone() == Zone.STACK && zEvent.getToZone() == Zone.GRAVEYARD && event.getSourceId() != null) { - if (event.getSourceId().equals(event.getTargetId())) { + if (event.getSourceId().equals(event.getTargetId()) && mor.getZoneChangeCounter() == game.getState().getZoneChangeCounter(event.getSourceId())) { Spell spell = game.getStack().getSpell(mor.getSourceId()); - if (spell != null && spell.isInstantOrSorcery()) { - return true; - } + return spell != null && spell.isInstantOrSorcery(); } } return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/FeatherTheRedeemedTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/FeatherTheRedeemedTest.java new file mode 100644 index 00000000000..bc353d8bd08 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/FeatherTheRedeemedTest.java @@ -0,0 +1,121 @@ +package org.mage.test.cards.abilities.other; + +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneAllEffect; +import mage.constants.Duration; +import mage.constants.PhaseStep; +import mage.constants.TargetController; +import mage.constants.Zone; +import mage.filter.StaticFilters; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ +public class FeatherTheRedeemedTest extends CardTestPlayerBase { + + // Feather, the Redeemed {R}{W}{W} + /* + Whenever you cast an instant or sorcery spell that targets a creature you control, exile that card + instead of putting it into your graveyard as it resolves. If you do, return it to your hand at the beginning of the next end step. + */ + + @Test + public void test_ExileSpellWithReturnAtTheEnd() { + // cast bolt, put to exile, return to hand + addCard(Zone.BATTLEFIELD, playerA, "Feather, the Redeemed"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 2); + + // cast and put to exile + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + checkPermanentCount("turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Grizzly Bears", 1); + checkExileCount("turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", 1); + checkHandCardCount("turn 1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + // return to hand at the next end step + checkExileCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + checkHandCardCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ExileSpellAndRecastWithReturnAtTheEnd() { + // cast bolt, put to exile, cast from exile, put to exile, return to hand + addCustomCardWithAbility("cast from exile", playerA, new SimpleStaticAbility( + new PlayFromNotOwnHandZoneAllEffect(StaticFilters.FILTER_CARD, Zone.EXILED, false, TargetController.ANY, Duration.WhileOnBattlefield) + )); + + addCard(Zone.BATTLEFIELD, playerA, "Feather, the Redeemed"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 3); + + // cast and put to exile + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 3 - 1); + checkExileCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + checkHandCardCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + // cast from exile and put to exile again + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 3 - 2); + checkExileCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + checkHandCardCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + // return to hand at the next end step + setChoice(playerA, "At the beginning of the next end step"); // two triggeres from two cast (card's code adds two same effects on each trigger) + checkExileCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + checkHandCardCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_ExileSpellAndRecastWithoutReturn() { + // cast bolt, put to exile, cast from exile with different target, put to graveyard, not return + addCustomCardWithAbility("cast from exile", playerA, new SimpleStaticAbility( + new PlayFromNotOwnHandZoneAllEffect(StaticFilters.FILTER_CARD, Zone.EXILED, false, TargetController.ANY, Duration.WhileOnBattlefield) + )); + + addCard(Zone.BATTLEFIELD, playerA, "Feather, the Redeemed"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + addCard(Zone.BATTLEFIELD, playerA, "Grizzly Bears", 3); + + // cast and put to exile + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Grizzly Bears"); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 3 - 1); + checkExileCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 1); + checkHandCardCount("turn 1", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + // cast from exile to target player (without trigger) and put to graveyard + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + checkPermanentCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Grizzly Bears", 3 - 1); // no changes + checkLife("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerB, 20 - 3); + checkExileCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + checkHandCardCount("turn 1 recast", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + // not return to hand at the next end step + checkExileCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + checkHandCardCount("turn 1 after", 2, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", 0); + + setStrictChooseMode(true); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + assertAllCommandsUsed(); + } +}