* 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.

This commit is contained in:
LevelX2 2015-08-02 10:34:57 +02:00
parent 6faeb19079
commit 2df109cf0b
9 changed files with 231 additions and 21 deletions

View file

@ -55,7 +55,7 @@ public class AEtherling extends CardImpl {
this.toughness = new MageInt(5); 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. // {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 // {U}: AEtherling can't be blocked this turn
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedSourceEffect(), new ManaCostsImpl("{U}"))); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBeBlockedSourceEffect(), new ManaCostsImpl("{U}")));
// {1}: AEtherling gets +1/-1 until end of turn. // {1}: AEtherling gets +1/-1 until end of turn.

View file

@ -58,7 +58,7 @@ public class Saltskitter extends CardImpl {
this.toughness = new MageInt(4); 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. // 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) { public Saltskitter(final Saltskitter card) {

View file

@ -31,8 +31,9 @@ import java.util.UUID;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ReturnFromExileEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
@ -45,6 +46,7 @@ import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.players.Player; import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil; import mage.util.CardUtil;
/** /**
@ -94,22 +96,23 @@ class GhostwayEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game); MageObject sourceObject = source.getSourceObject(game);
if (sourceObject != null && controller != null) { if (sourceObject != null && controller != null) {
int numberCreatures = 0;
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) {
if (creature != null) { if (creature != null) {
controller.moveCardToExileWithInfo(creature, exileId,sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true); int zcc = game.getState().getZoneChangeCounter(creature.getId());
numberCreatures++; controller.moveCardToExileWithInfo(creature, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.BATTLEFIELD, true);
} if (zcc == game.getState().getZoneChangeCounter(creature.getId()) - 1) {
} Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect();
if (numberCreatures > 0) { effect.setTargetPointer(new FixedTarget(creature.getId(), zcc + 1));
AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility( AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect);
new ReturnFromExileEffect(exileId, Zone.BATTLEFIELD, false));
delayedAbility.setSourceId(source.getSourceId()); delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId()); delayedAbility.setControllerId(source.getControllerId());
delayedAbility.setSourceObject(source.getSourceObject(game), game); delayedAbility.setSourceObject(source.getSourceObject(game), game);
game.addDelayedTriggeredAbility(delayedAbility); game.addDelayedTriggeredAbility(delayedAbility);
} }
}
}
return true; return true;
} }
return false; return false;

View file

@ -55,7 +55,7 @@ public class AnuridBrushhopper extends CardImpl {
// Discard two cards: Exile Anurid Brushhopper. Return it to the battlefield under its owner's control at the beginning of the next end step. // 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, this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
new ExileReturnToBattlefieldOwnerNextEndStepEffect(), new ExileReturnToBattlefieldOwnerNextEndStepEffect(true),
new DiscardTargetCost(new TargetCardInHand(2, new FilterCard("two cards"))))); new DiscardTargetCost(new TargetCardInHand(2, new FilterCard("two cards")))));
} }

View file

@ -97,7 +97,7 @@ class FreneticSliverEffect extends OneShotEffect {
Player player = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getControllerId());
if (player != null) { if (player != null) {
if (player.flipCoin(game)) { if (player.flipCoin(game)) {
return new ExileReturnToBattlefieldOwnerNextEndStepEffect().apply(game, source); return new ExileReturnToBattlefieldOwnerNextEndStepEffect(true).apply(game, source);
} else { } else {
Permanent perm = game.getPermanent(source.getSourceId()); Permanent perm = game.getPermanent(source.getSourceId());
if (perm != null) { if (perm != null) {

View file

@ -73,7 +73,7 @@ public class NorinTheWary extends CardImpl {
class NorinTheWaryTriggeredAbility extends TriggeredAbilityImpl { class NorinTheWaryTriggeredAbility extends TriggeredAbilityImpl {
public NorinTheWaryTriggeredAbility() { public NorinTheWaryTriggeredAbility() {
super(Zone.BATTLEFIELD, new ExileReturnToBattlefieldOwnerNextEndStepEffect(), false); super(Zone.BATTLEFIELD, new ExileReturnToBattlefieldOwnerNextEndStepEffect(true), false);
} }
public NorinTheWaryTriggeredAbility(final NorinTheWaryTriggeredAbility ability) { public NorinTheWaryTriggeredAbility(final NorinTheWaryTriggeredAbility ability) {

View file

@ -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

View file

@ -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);
}
}

View file

@ -43,14 +43,27 @@ import mage.players.Player;
public class ExileReturnToBattlefieldOwnerNextEndStepEffect extends OneShotEffect { 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 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() { 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); super(Outcome.Benefit);
staticText = effectText; staticText = effectText;
this.returnAlways = returnAlways;
} }
public ExileReturnToBattlefieldOwnerNextEndStepEffect(ExileReturnToBattlefieldOwnerNextEndStepEffect effect) { public ExileReturnToBattlefieldOwnerNextEndStepEffect(ExileReturnToBattlefieldOwnerNextEndStepEffect effect) {
super(effect); super(effect);
this.returnAlways = effect.returnAlways;
} }
@Override @Override
@ -60,7 +73,8 @@ public class ExileReturnToBattlefieldOwnerNextEndStepEffect extends OneShotEffec
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null) { if (permanent != null) {
int zcc = game.getState().getZoneChangeCounter(permanent.getId()); 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 //create delayed triggered ability and return it from every public zone he was next moved to
AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility( AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
new ReturnToBattlefieldUnderOwnerControlSourceEffect(false, zcc + 1)); new ReturnToBattlefieldUnderOwnerControlSourceEffect(false, zcc + 1));