diff --git a/Mage.Sets/src/mage/cards/a/ArcaneBombardment.java b/Mage.Sets/src/mage/cards/a/ArcaneBombardment.java index d692f32eebe..3441716b459 100644 --- a/Mage.Sets/src/mage/cards/a/ArcaneBombardment.java +++ b/Mage.Sets/src/mage/cards/a/ArcaneBombardment.java @@ -116,18 +116,16 @@ class ArcaneBombardmentEffect extends OneShotEffect { // Allow player to choose the order and cast the copies if (!copies.isEmpty()) { - TargetCard target = new TargetCard(0, copies.size(), Zone.EXILED, new FilterCard("copies to cast")); + TargetCard target = new TargetCard(0, copies.size(), Zone.EXILED, new FilterCard("copies to cast (in order they're chosen)")); player.choose(Outcome.PlayForFree, copies, target, source, game); List targets = target.getTargets(); for (UUID targetId : targets) { Card copiedCard = game.getCard(targetId); if (copiedCard != null && copiedCard.getSpellAbility() != null) { - if (player.chooseUse(Outcome.PlayForFree, "Cast the copy of " + copiedCard.getLogName() + "?", source, game)) { - game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), Boolean.TRUE); - player.cast(player.chooseAbilityForCast(copiedCard, game, true), game, true, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), null); - } + game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), Boolean.TRUE); + player.cast(player.chooseAbilityForCast(copiedCard, game, true), game, true, new ApprovingObject(source, game)); + game.getState().setValue("PlayFromNotOwnHandZone" + copiedCard.getId(), null); } } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HideawayTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HideawayTest.java index c8d3d97c835..5f8a7d47e9d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HideawayTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HideawayTest.java @@ -78,7 +78,7 @@ public class HideawayTest extends CardTestPlayerBase { setChoice(playerA, "Ulamog, the Ceaseless Hunger"); activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{G},"); - setChoice(playerA, true); // play Ghost Quarter + setChoice(playerA, "Ulamog, the Ceaseless Hunger"); // play Ulamog addTarget(playerA, "Dross Crocodile^Dross Crocodile"); @@ -124,7 +124,7 @@ public class HideawayTest extends CardTestPlayerBase { attack(3, playerA, "Auriok Champion"); activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerA, "{W},"); - setChoice(playerA, true); // play Ghost Quarter + setChoice(playerA, "Ghost Quarter"); // play Ghost Quarter setStopAt(3, PhaseStep.END_COMBAT); @@ -155,7 +155,7 @@ public class HideawayTest extends CardTestPlayerBase { setChoice(playerA, "Ghost Quarter"); activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerA, "{G},"); - setChoice(playerA, true); + setChoice(playerA, "Ghost Quarter"); setStopAt(4, PhaseStep.BEGIN_COMBAT); @@ -182,7 +182,7 @@ public class HideawayTest extends CardTestPlayerBase { attack(3, playerA, "Auriok Champion"); activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerA, "{W},"); - setChoice(playerA, true); // play Ghost Quarter + setChoice(playerA, "Ghost Quarter"); // play Ghost Quarter setStopAt(3, PhaseStep.END_COMBAT); @@ -215,7 +215,7 @@ public class HideawayTest extends CardTestPlayerBase { attack(3, playerA, "Auriok Champion"); activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerA, "{W},"); - setChoice(playerA, true); // play Ghost Quarter + setChoice(playerA, "Ghost Quarter"); // play Ghost Quarter setStopAt(3, PhaseStep.END_COMBAT); @@ -254,7 +254,7 @@ public class HideawayTest extends CardTestPlayerBase { playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, sIsle); setChoice(playerA, ulamog); activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{U}"); - setChoice(playerA, true); // play Ulamog's Crusher + setChoice(playerA, ulamog); // play Ulamog's Crusher setStopAt(3, PhaseStep.BEGIN_COMBAT); @@ -298,7 +298,7 @@ public class HideawayTest extends CardTestPlayerBase { playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, sIsle); setChoice(playerA, ulamog); activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{U}"); - setChoice(playerA, true); // play Ulamog's Crusher + setChoice(playerA, ulamog); // play Ulamog's Crusher setStopAt(3, PhaseStep.BEGIN_COMBAT); execute(); @@ -309,6 +309,115 @@ public class HideawayTest extends CardTestPlayerBase { assertPermanentCount(playerA, ulamog, 1); } + /** + * Rule 607.3 - if Hideaway trigger is copied, "the exiled card" refers to each card exiled by Hideaway abilities. + */ + @Test + public void testMultipleHideawayTriggers() { + addCard(Zone.HAND, playerA, "Windbrisk Heights"); + addCard(Zone.LIBRARY, playerA, "Llanowar Elves", 4); + addCard(Zone.LIBRARY, playerA, "Auriok Champion", 4); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Auriok Glaivemaster", 2); + addCard(Zone.BATTLEFIELD, playerA, "Elesh Norn, Mother of Machines"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Windbrisk Heights"); + setChoice(playerA, "Hideaway 4"); // Order Hideaway triggers + setChoice(playerA, "Auriok Champion"); + setChoice(playerA, "Llanowar Elves"); + + attack(3, playerA, "Auriok Glaivemaster"); + attack(3, playerA, "Auriok Glaivemaster"); + attack(3, playerA, "Elesh Norn, Mother of Machines"); + + activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerA, "{W},"); + setChoice(playerA, "Llanowar Elves^Auriok Champion"); // play Llanowar Elves, then Auriok Champion (goes on stack second, resolves first) + setChoice(playerA, "Whenever"); // Order Auriok Champion's two gain life triggers thanks to Elesh Norn + setChoice(playerA, true); // Gain life + setChoice(playerA, true); // Gain life + + setStopAt(3, PhaseStep.END_COMBAT); + + setStrictChooseMode(true); + execute(); + + assertPermanentCount(playerA, "Llanowar Elves", 1); + assertPermanentCount(playerA, "Auriok Champion", 1); + assertLife(playerA, 22); // Gained a life from Auriok Champion resolving first. + assertTapped("Windbrisk Heights", true); + } + + /** + * Hideaway two lands, attempt to play both exiled lands, only the first one succeeds. + */ + @Test + public void testMultipleHideawayTriggersPlayOneLand() { + addCard(Zone.HAND, playerA, "Windbrisk Heights"); + addCard(Zone.LIBRARY, playerA, "Field of the Dead", 4); + addCard(Zone.LIBRARY, playerA, "Ghost Quarter", 4); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Auriok Champion", 3); + addCard(Zone.BATTLEFIELD, playerA, "Elesh Norn, Mother of Machines"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Windbrisk Heights"); + setChoice(playerA, "Hideaway 4"); // Order Hideaway triggers + setChoice(playerA, "Ghost Quarter"); + setChoice(playerA, "Field of the Dead"); + + attack(3, playerA, "Auriok Champion"); + attack(3, playerA, "Auriok Champion"); + attack(3, playerA, "Auriok Champion"); + + activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerA, "{W},"); + setChoice(playerA, "Ghost Quarter^Field of the Dead"); // play Ghost Quarter, attempt to play Field of the Dead + + setStopAt(3, PhaseStep.END_COMBAT); + + setStrictChooseMode(true); + execute(); + + assertPermanentCount(playerA, "Ghost Quarter", 1); + assertTapped("Windbrisk Heights", true); + assertExileCount(playerA, 1); + Assert.assertEquals(1, playerA.getLandsPlayed()); + } + + @Test + public void testMultipleHideawayTriggersPlayMultipleLands() { + addCard(Zone.HAND, playerA, "Windbrisk Heights"); + addCard(Zone.LIBRARY, playerA, "Ghost Quarter", 5); + skipInitShuffling(); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Auriok Champion", 3); + addCard(Zone.BATTLEFIELD, playerA, "Elesh Norn, Mother of Machines"); + addCard(Zone.BATTLEFIELD, playerA, "Fastbond", 1); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Windbrisk Heights"); + setChoice(playerA, "Hideaway 4"); // Order Hideaway triggers + setChoice(playerA, "Ghost Quarter", 2); + + attack(3, playerA, "Auriok Champion"); + attack(3, playerA, "Auriok Champion"); + attack(3, playerA, "Auriok Champion"); + + activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerA, "{W},"); + setChoice(playerA, "Ghost Quarter^Ghost Quarter"); // play Ghost Quarter + + setStopAt(3, PhaseStep.END_COMBAT); + + setStrictChooseMode(true); + execute(); + + assertPermanentCount(playerA, "Ghost Quarter", 2); + assertTapped("Windbrisk Heights", true); + Assert.assertEquals(2, playerA.getLandsPlayed()); + } + /** * Watcher for tomorrow - Watcher of Tomorrow not working when been blinked * by any source, like Ephemerate or Soulherder, still working if dies diff --git a/Mage/src/main/java/mage/abilities/effects/common/HideawayPlayEffect.java b/Mage/src/main/java/mage/abilities/effects/common/HideawayPlayEffect.java index 81389a98898..9dbcfb8aac0 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/HideawayPlayEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/HideawayPlayEffect.java @@ -4,12 +4,17 @@ import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; +import mage.cards.CardsImpl; import mage.constants.Outcome; +import mage.constants.Zone; +import mage.filter.FilterCard; import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; +import mage.target.TargetCard; import mage.util.CardUtil; +import java.util.List; import java.util.UUID; /** @@ -17,13 +22,25 @@ import java.util.UUID; */ public class HideawayPlayEffect extends OneShotEffect { + private final boolean playOne; + public HideawayPlayEffect() { + this(false); + } + + public HideawayPlayEffect(boolean playOne) { super(Outcome.Benefit); - staticText = "you may play the exiled card without paying its mana cost"; + this.playOne = playOne; + if (playOne) { + staticText = "if there are cards exiled with it, you may play one of them without paying its mana cost"; + } else { + staticText = "you may play the exiled card without paying its mana cost"; + } } protected HideawayPlayEffect(final HideawayPlayEffect effect) { super(effect); + this.playOne = effect.playOne; } @Override @@ -38,33 +55,39 @@ public class HideawayPlayEffect extends OneShotEffect { if (controller == null || zone == null || zone.isEmpty()) { return true; } - Card card = zone.getRandom(game); - if (card == null) { - return false; - } - if (!controller.chooseUse(Outcome.PlayForFree, "Play " + card.getIdName() + " for free?", source, game)) { - return false; - } - card.setFaceDown(false, game); - int zcc = card.getZoneChangeCounter(game); + CardsImpl cards = new CardsImpl(zone.getCards(game)); - /* 702.74. Hideaway, rulings: - * If the removed card is a land, you may play it as a result of the last ability only if it's your turn - * and you haven't already played a land that turn. This counts as your land play for the turn. - * TODO: this doesn't work correctly with the back half of MDFCs - */ - if (card.isLand(game)) { - UUID playerId = controller.getId(); - if (!game.isActivePlayer(playerId) || !game.getPlayer(playerId).canPlayLand()) { - return false; + boolean cardPlayed = false; + int maxChoices = (this.playOne) ? 1 : cards.size(); + TargetCard target = new TargetCard(0, maxChoices, Zone.EXILED, new FilterCard("cards to play (in order they're chosen)")); + controller.choose(Outcome.PlayForFree, cards, target, source, game); + List targets = target.getTargets(); + + for (UUID targetId : targets) { + Card card = game.getCard(targetId); + /* 702.74. Hideaway, rulings: + * If the removed card is a land, you may play it as a result of the last ability only if it's your turn + * and you haven't already played a land that turn. This counts as your land play for the turn. + * TODO: this doesn't work correctly with the back half of MDFCs + */ + if (card.isLand(game)) { + UUID playerId = controller.getId(); + if (!game.isActivePlayer(playerId) || !game.getPlayer(playerId).canPlayLand()) { + continue; + } + } + + card.setFaceDown(false, game); + int zcc = card.getZoneChangeCounter(game); + + if (controller.playCard(card, game, true, new ApprovingObject(source, game))) { + cardPlayed = true; + } else { + if (card.getZoneChangeCounter(game) == zcc) { + card.setFaceDown(true, game); + } } } - - if (!controller.playCard(card, game, true, new ApprovingObject(source, game))) { - if (card.getZoneChangeCounter(game) == zcc) { - card.setFaceDown(true, game); - } - } - return true; + return cardPlayed; } }