mirror of
https://github.com/correl/mage.git
synced 2024-11-25 03:00:11 +00:00
* 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:
parent
6faeb19079
commit
2df109cf0b
9 changed files with 231 additions and 21 deletions
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD,
|
||||
new ExileReturnToBattlefieldOwnerNextEndStepEffect(),
|
||||
new ExileReturnToBattlefieldOwnerNextEndStepEffect(true),
|
||||
new DiscardTargetCost(new TargetCardInHand(2, new FilterCard("two cards")))));
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
75
Mage.Tests/CMDNorinTheWary.dck
Normal file
75
Mage.Tests/CMDNorinTheWary.dck
Normal 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
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
|
Loading…
Reference in a new issue