From 2df109cf0bd72814fc406d38b45e227de6af5ee0 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 2 Aug 2015 10:34:57 +0200 Subject: [PATCH] * Norin the Wary - Fixed that it also returned from commandzone if put there from commander replament effect. Some other move and return from exile fixes. --- .../src/mage/sets/dragonsmaze/AEtherling.java | 2 +- .../mage/sets/futuresight/Saltskitter.java | 6 +- .../src/mage/sets/guildpact/Ghostway.java | 27 ++-- .../mage/sets/judgment/AnuridBrushhopper.java | 4 +- .../mage/sets/planarchaos/FreneticSliver.java | 2 +- .../mage/sets/timespiral/NorinTheWary.java | 2 +- Mage.Tests/CMDNorinTheWary.dck | 75 +++++++++++ .../test/commander/duel/NorinTheWaryTest.java | 118 ++++++++++++++++++ ...rnToBattlefieldOwnerNextEndStepEffect.java | 16 ++- 9 files changed, 231 insertions(+), 21 deletions(-) create mode 100644 Mage.Tests/CMDNorinTheWary.dck create mode 100644 Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/AEtherling.java b/Mage.Sets/src/mage/sets/dragonsmaze/AEtherling.java index a79869e862..3dd30d6f2a 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/AEtherling.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/AEtherling.java @@ -55,7 +55,7 @@ public class AEtherling extends CardImpl { this.toughness = new MageInt(5); // {U}: Exile AEtherling. Return it to the battlefield under its owner's control at the beginning of the next end step. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileReturnToBattlefieldOwnerNextEndStepEffect(), new ManaCostsImpl("{U}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileReturnToBattlefieldOwnerNextEndStepEffect(true), new ManaCostsImpl("{U}"))); // {U}: AEtherling can't be blocked this turn this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedSourceEffect(), new ManaCostsImpl("{U}"))); // {1}: AEtherling gets +1/-1 until end of turn. diff --git a/Mage.Sets/src/mage/sets/futuresight/Saltskitter.java b/Mage.Sets/src/mage/sets/futuresight/Saltskitter.java index 70f9bc4bd4..f4e73a800f 100644 --- a/Mage.Sets/src/mage/sets/futuresight/Saltskitter.java +++ b/Mage.Sets/src/mage/sets/futuresight/Saltskitter.java @@ -43,9 +43,9 @@ import mage.filter.predicate.permanent.AnotherPredicate; * @author fireshoes */ public class Saltskitter extends CardImpl { - + private static final FilterPermanent filter = new FilterCreaturePermanent("another creature"); - + static { filter.add(new AnotherPredicate()); } @@ -58,7 +58,7 @@ public class Saltskitter extends CardImpl { this.toughness = new MageInt(4); // Whenever another creature enters the battlefield, exile Saltskitter. Return Saltskitter to the battlefield under its owner's control at the beginning of the next end step. - this.addAbility(new EntersBattlefieldAllTriggeredAbility(new ExileReturnToBattlefieldOwnerNextEndStepEffect(), filter)); + this.addAbility(new EntersBattlefieldAllTriggeredAbility(new ExileReturnToBattlefieldOwnerNextEndStepEffect(true), filter)); } public Saltskitter(final Saltskitter card) { diff --git a/Mage.Sets/src/mage/sets/guildpact/Ghostway.java b/Mage.Sets/src/mage/sets/guildpact/Ghostway.java index 776e27e8ac..ab0e57b2cc 100644 --- a/Mage.Sets/src/mage/sets/guildpact/Ghostway.java +++ b/Mage.Sets/src/mage/sets/guildpact/Ghostway.java @@ -31,8 +31,9 @@ import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.ReturnFromExileEffect; +import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -45,6 +46,7 @@ import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; +import mage.target.targetpointer.FixedTarget; import mage.util.CardUtil; /** @@ -94,22 +96,23 @@ class GhostwayEffect extends OneShotEffect { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); if (sourceObject != null && controller != null) { - int numberCreatures = 0; UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { if (creature != null) { - controller.moveCardToExileWithInfo(creature, exileId,sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true); - numberCreatures++; + int zcc = game.getState().getZoneChangeCounter(creature.getId()); + controller.moveCardToExileWithInfo(creature, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true); + if (zcc == game.getState().getZoneChangeCounter(creature.getId()) - 1) { + Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect(); + effect.setTargetPointer(new FixedTarget(creature.getId(), zcc + 1)); + AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect); + + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.setSourceObject(source.getSourceObject(game), game); + game.addDelayedTriggeredAbility(delayedAbility); + } } } - if (numberCreatures > 0) { - AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility( - new ReturnFromExileEffect(exileId, Zone.BATTLEFIELD, false)); - delayedAbility.setSourceId(source.getSourceId()); - delayedAbility.setControllerId(source.getControllerId()); - delayedAbility.setSourceObject(source.getSourceObject(game), game); - game.addDelayedTriggeredAbility(delayedAbility); - } return true; } return false; diff --git a/Mage.Sets/src/mage/sets/judgment/AnuridBrushhopper.java b/Mage.Sets/src/mage/sets/judgment/AnuridBrushhopper.java index e068569469..d3be1aaddb 100644 --- a/Mage.Sets/src/mage/sets/judgment/AnuridBrushhopper.java +++ b/Mage.Sets/src/mage/sets/judgment/AnuridBrushhopper.java @@ -54,8 +54,8 @@ public class AnuridBrushhopper extends CardImpl { this.toughness = new MageInt(4); // Discard two cards: Exile Anurid Brushhopper. Return it to the battlefield under its owner's control at the beginning of the next end step. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, - new ExileReturnToBattlefieldOwnerNextEndStepEffect(), + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, + new ExileReturnToBattlefieldOwnerNextEndStepEffect(true), new DiscardTargetCost(new TargetCardInHand(2, new FilterCard("two cards"))))); } diff --git a/Mage.Sets/src/mage/sets/planarchaos/FreneticSliver.java b/Mage.Sets/src/mage/sets/planarchaos/FreneticSliver.java index 8a0c4422a4..bcc9c5e9fc 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/FreneticSliver.java +++ b/Mage.Sets/src/mage/sets/planarchaos/FreneticSliver.java @@ -97,7 +97,7 @@ class FreneticSliverEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); if (player != null) { if (player.flipCoin(game)) { - return new ExileReturnToBattlefieldOwnerNextEndStepEffect().apply(game, source); + return new ExileReturnToBattlefieldOwnerNextEndStepEffect(true).apply(game, source); } else { Permanent perm = game.getPermanent(source.getSourceId()); if (perm != null) { diff --git a/Mage.Sets/src/mage/sets/timespiral/NorinTheWary.java b/Mage.Sets/src/mage/sets/timespiral/NorinTheWary.java index b87f928ade..1b0a67fb13 100644 --- a/Mage.Sets/src/mage/sets/timespiral/NorinTheWary.java +++ b/Mage.Sets/src/mage/sets/timespiral/NorinTheWary.java @@ -73,7 +73,7 @@ public class NorinTheWary extends CardImpl { class NorinTheWaryTriggeredAbility extends TriggeredAbilityImpl { public NorinTheWaryTriggeredAbility() { - super(Zone.BATTLEFIELD, new ExileReturnToBattlefieldOwnerNextEndStepEffect(), false); + super(Zone.BATTLEFIELD, new ExileReturnToBattlefieldOwnerNextEndStepEffect(true), false); } public NorinTheWaryTriggeredAbility(final NorinTheWaryTriggeredAbility ability) { diff --git a/Mage.Tests/CMDNorinTheWary.dck b/Mage.Tests/CMDNorinTheWary.dck new file mode 100644 index 0000000000..74c3bd1c17 --- /dev/null +++ b/Mage.Tests/CMDNorinTheWary.dck @@ -0,0 +1,75 @@ +1 [BOK:108] In the Web of War +1 [7ED:319] Static Orb +1 [ALA:101] Goblin Assault +1 [8ED:204] Obliterate +1 [ORI:149] Ghirapur Gearcrafter +1 [SCG:85] Decree of Annihilation +1 [ONS:317] Forgotten Cave +1 [MRD:282] Great Furnace +1 [EVE:179] Springjack Pasture +1 [VIS:141] Anvil of Bogardan +1 [JOU:89] Bearer of the Heavens +1 [ODY:214] Price of Glory +1 [9ED:176] Blood Moon +1 [M10:212] Howling Mine +1 [CMD:114] Chaos Warp +1 [M12:153] Scrambleverse +1 [10E:204] Furnace of Rath +1 [ZEN:228] Valakut, the Molten Pinnacle +1 [M12:160] Warstorm Surge +1 [AVR:126] Archwing Dragon +1 [M15:149] Hoarding Dragon +1 [4ED:189] Strip Mine +1 [ORI:145] Fiery Impulse +1 [CSP:149] Scrying Sheets +1 [ORI:142] Enthralling Victor +1 [ORI:144] Fiery Conclusion +1 [ORI:139] Demolish +1 [MMQ:320] High Market +1 [M12:224] Buried Ruin +1 [TOR:113] Radiate +1 [M10:163] Warp World +1 [TSP:188] Word of Seizing +1 [DST:122] Genesis Chamber +1 [TSP:275] Kher Keep +1 [ORI:134] Call of the Full Moon +1 [ORI:136] Chandra's Fury +26 [CSP:154] Snow-Covered Mountain +1 [ONS:212] Gratuitous Violence +1 [ORI:133] Boggart Brute +1 [ONS:213] Insurrection +1 [TMP:210] Tooth and Claw +1 [ORI:129] Act of Treason +1 [BOK:120] Twist Allegiance +1 [CMD:136] Stranglehold +1 [M13:145] Reverberate +1 [EXO:102] Shattering Pulse +1 [9ED:312] Teferi's Puzzle Box +1 [M14:162] Wild Ricochet +1 [3ED:274] Sol Ring +1 [3ED:152] Fork +1 [DGM:34] Possibility Storm +1 [USG:188] Gamble +1 [5ED:64] Smoke +1 [STH:93] Mogg Infestation +1 [ULG:80] Goblin Welder +1 [ALA:119] Vicious Shadows +1 [ORI:163] Smash to Smithereens +1 [THS:135] Purphoros, God of the Forge +1 [CON:136] Font of Mythos +1 [7ED:307] Meekstone +1 [TSB:68] Pandemonium +1 [GTC:248] Thespian's Stage +1 [3ED:184] Wheel of Fortune +1 [M15:242] Darksteel Citadel +1 [M11:160] Wild Evocation +1 [RTR:111] Vandalblast +1 [TSP:175] Reiterate +1 [M14:148] Ogre Battledriver +1 [ORI:156] Molten Vortex +1 [MIR:183] Illicit Auction +1 [ULG:123] Crawlspace +1 [5ED:246] Jokulhaups +1 [JOU:93] Dictate of the Twin Gods +1 [5ED:249] Mana Flare +SB: 1 [TSP:171] Norin the Wary diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java new file mode 100644 index 0000000000..cb5e232b7e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/NorinTheWaryTest.java @@ -0,0 +1,118 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package org.mage.test.commander.duel; + +import java.io.FileNotFoundException; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.GameException; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * + * @author LevelX2 + */ +public class NorinTheWaryTest extends CardTestCommanderDuelBase { + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + // When a player casts a spell or a creature attacks, exile Norin the Wary. Return it to the battlefield under its owner's control at the beginning of the next end step. + setDecknamePlayerA("CMDNorinTheWary.dck"); // Commander = Norin the Wary {R} + return super.createNewGameAndPlayers(); + } + + @Test + public void castNorinTheWary() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Norin the Wary"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Norin the Wary", 1); + } + + @Test + public void castNorinTheWaryToExile() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Norin the Wary"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertPermanentCount(playerA, "Norin the Wary", 0); + assertExileCount("Norin the Wary", 1); + + assertLife(playerB, 37); + + } + + @Test + public void castNorinTheWaryToExileAndReturn() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Norin the Wary"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "No"); + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertPermanentCount(playerA, "Norin the Wary", 1); + assertExileCount("Norin the Wary", 0); + + assertLife(playerB, 37); + + } + + @Test + public void castNorinTheWaryToCommandAndReturn() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + addCard(Zone.HAND, playerA, "Lightning Bolt", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Norin the Wary"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Lightning Bolt", playerB); + setChoice(playerA, "Yes"); + setStopAt(2, PhaseStep.UPKEEP); + execute(); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertPermanentCount(playerA, "Norin the Wary", 1); + assertExileCount("Norin the Wary", 0); + + assertLife(playerB, 37); + + } +} diff --git a/Mage/src/mage/abilities/effects/common/ExileReturnToBattlefieldOwnerNextEndStepEffect.java b/Mage/src/mage/abilities/effects/common/ExileReturnToBattlefieldOwnerNextEndStepEffect.java index a49b20fd83..e608d796bf 100644 --- a/Mage/src/mage/abilities/effects/common/ExileReturnToBattlefieldOwnerNextEndStepEffect.java +++ b/Mage/src/mage/abilities/effects/common/ExileReturnToBattlefieldOwnerNextEndStepEffect.java @@ -43,14 +43,27 @@ import mage.players.Player; public class ExileReturnToBattlefieldOwnerNextEndStepEffect extends OneShotEffect { private static final String effectText = "exile {this}. Return it to the battlefield under its owner's control at the beginning of the next end step"; + private boolean returnAlways; public ExileReturnToBattlefieldOwnerNextEndStepEffect() { + this(false); + } + + /** + * + * @param returnAlways return the permanent also if it does not go to exile + * but is moved to another zone (e.g. command zone by commander replacement + * effect) + */ + public ExileReturnToBattlefieldOwnerNextEndStepEffect(boolean returnAlways) { super(Outcome.Benefit); staticText = effectText; + this.returnAlways = returnAlways; } public ExileReturnToBattlefieldOwnerNextEndStepEffect(ExileReturnToBattlefieldOwnerNextEndStepEffect effect) { super(effect); + this.returnAlways = effect.returnAlways; } @Override @@ -60,7 +73,8 @@ public class ExileReturnToBattlefieldOwnerNextEndStepEffect extends OneShotEffec Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { int zcc = game.getState().getZoneChangeCounter(permanent.getId()); - if (controller.moveCardToExileWithInfo(permanent, source.getSourceId(), permanent.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true)) { + boolean exiled = controller.moveCardToExileWithInfo(permanent, source.getSourceId(), permanent.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true); + if (exiled || (returnAlways && (zcc == game.getState().getZoneChangeCounter(permanent.getId()) - 1))) { //create delayed triggered ability and return it from every public zone he was next moved to AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility( new ReturnToBattlefieldUnderOwnerControlSourceEffect(false, zcc + 1));