From 098796f86e9ceb16af6df87840741666d0255f96 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Mon, 1 Mar 2021 01:14:00 +0400 Subject: [PATCH] * Commander: fixed that non hand abilities are castable from command zone (example: Escape, Jumpstart, see #7632); --- .../mage/cards/u/UroTitanOfNaturesWrath.java | 4 +-- .../cards/continuous/CommandersCastTest.java | 33 +++++++++++++++++++ .../java/mage/game/command/Commander.java | 28 +++++++++++----- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java b/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java index 1f3853c1d2..7051cd1de4 100644 --- a/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java +++ b/Mage.Sets/src/mage/cards/u/UroTitanOfNaturesWrath.java @@ -40,8 +40,8 @@ public final class UroTitanOfNaturesWrath extends CardImpl { // Whenever Uro enters the battlefield or attacks, you gain 3 life and draw a card, then you may put a land card from your hand onto the battlefield. Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new GainLifeEffect(3)); - ability.addEffect(new DrawCardSourceControllerEffect(1).setText("and draw a card, then")); - ability.addEffect(new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A)); + ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and")); + ability.addEffect(new PutCardFromHandOntoBattlefieldEffect(StaticFilters.FILTER_CARD_LAND_A).concatBy(", then")); this.addAbility(ability); // Escape-{G}{G}{U}{U}, Exile five other cards from your graveyard. diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java index 690b387fd2..9e64b5da68 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/CommandersCastTest.java @@ -578,4 +578,37 @@ public class CommandersCastTest extends CardTestCommander4Players { execute(); assertAllCommandsUsed(); } + + @Test + public void test_Escape_CantBeCastableFromCommandZone() { + // Player order: A -> D -> C -> B + + // When Uro enters the battlefield, sacrifice it unless it escaped. + // Whenever Uro enters the battlefield or attacks, you gain 3 life and draw a card, then you may put a land card from your hand onto the battlefield. + // Escape-{G}{G}{U}{U}, Exile five other cards from your graveyard. + addCard(Zone.COMMAND, playerA, "Uro, Titan of Nature's Wrath", 1); // {1}{G}{U} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + // + addCard(Zone.GRAVEYARD, playerA, "Grizzly Bears", 5); + addCard(Zone.HAND, playerA, "Swamp", 1); + + checkPlayableAbility("normal cast allowed", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Uro, Titan of Nature's Wrath", true); + checkPlayableAbility("escape cast not allowed", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Uro, Titan of Nature's Wrath with Escape", false); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Uro, Titan of Nature's Wrath"); + setChoice(playerA, "Whenever {this} enters the battlefield or attacks"); // gain life trigger first, sacrifice next + setChoice(playerA, "No"); // keep in graveyard + setChoice(playerA, "Yes"); // put land to battlefield + setChoice(playerA, "Swamp"); // put a Swamp + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 20 + 3); + assertPermanentCount(playerA, "Swamp", 1); + assertGraveyardCount(playerA, "Uro, Titan of Nature's Wrath", 1); // sacrificed + } } diff --git a/Mage/src/main/java/mage/game/command/Commander.java b/Mage/src/main/java/mage/game/command/Commander.java index 706546312d..1e21c63979 100644 --- a/Mage/src/main/java/mage/game/command/Commander.java +++ b/Mage/src/main/java/mage/game/command/Commander.java @@ -8,7 +8,6 @@ import mage.abilities.common.CastCommanderAbility; import mage.abilities.common.PlayLandAsCommanderAbility; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.keyword.EscapeAbility; import mage.abilities.text.TextPart; import mage.cards.Card; import mage.cards.FrameStyle; @@ -44,10 +43,6 @@ public class Commander implements CommandObject { switch (spellAbility.getSpellAbilityType()) { case BASE: case BASE_ALTERNATE: - // Escape only castable from graveyard - if (ability instanceof EscapeAbility) { - break; - } case SPLIT: case SPLIT_FUSED: case SPLIT_LEFT: @@ -57,7 +52,9 @@ public class Commander implements CommandObject { case MODAL_RIGHT: case ADVENTURE_SPELL: // can be used from command zone - abilities.add(new CastCommanderAbility(card, spellAbility)); + if (canUseAbilityFromCommandZone(spellAbility)) { + abilities.add(new CastCommanderAbility(card, spellAbility)); + } break; case FACE_DOWN_CREATURE: // dynamic added spell for alternative cost like cast as face down case SPLICE: // only from hand @@ -73,8 +70,10 @@ public class Commander implements CommandObject { // replace play land with commander play land (to play from command zone) for (Ability ability : card.getAbilities()) { if (ability instanceof PlayLandAbility) { - Ability newAbility = new PlayLandAsCommanderAbility((PlayLandAbility) ability); - abilities.add(newAbility); + if (canUseAbilityFromCommandZone(ability)) { + Ability newAbility = new PlayLandAsCommanderAbility((PlayLandAbility) ability); + abilities.add(newAbility); + } } } @@ -86,11 +85,24 @@ public class Commander implements CommandObject { } // all other abilities must be added to commander (example: triggers from command zone, alternative cost, etc) + // no changes to ability zone, so can add any Ability newAbility = ability.copy(); abilities.add(newAbility); } } + private boolean canUseAbilityFromCommandZone(Ability ability) { + // ability can be restricted by zone usage, so you must ignore it for commander (example: Escape or Jumpstart) + switch (ability.getZone()) { + case ALL: + case COMMAND: + case HAND: + return true; + default: + return false; + } + } + private Commander(final Commander commander) { this.sourceObject = commander.sourceObject; this.copy = commander.copy;