diff --git a/Mage.Sets/src/mage/sets/alarareborn/MaelstromNexus.java b/Mage.Sets/src/mage/sets/alarareborn/MaelstromNexus.java index 26c7043aec0..fb52e23379e 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/MaelstromNexus.java +++ b/Mage.Sets/src/mage/sets/alarareborn/MaelstromNexus.java @@ -31,18 +31,16 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.keyword.CascadeAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.WatcherScope; import mage.constants.Zone; -import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.Spell; -import mage.players.Player; import mage.target.targetpointer.FixedTarget; import mage.watchers.Watcher; @@ -56,12 +54,6 @@ public class MaelstromNexus extends CardImpl { super(ownerId, 130, "Maelstrom Nexus", Rarity.MYTHIC, new CardType[]{CardType.ENCHANTMENT}, "{W}{U}{B}{R}{G}"); this.expansionSetCode = "ARB"; - - - - - - // The first spell you cast each turn has cascade. this.addAbility(new MaelstromNexusTriggeredAbility(), new FirstSpellCastThisTurnWatcher()); @@ -164,40 +156,7 @@ class CascadeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Card card; - Player player = game.getPlayer(source.getControllerId()); - if (player == null) { - return false; - } - ExileZone exile = game.getExile().createZone(source.getSourceId(), player.getName() + " Cascade"); - Card stackCard = game.getCard(targetPointer.getFirst(game, source)); - if (stackCard == null) { - return false; - } - int sourceCost = stackCard.getManaCost().convertedManaCost(); - do { - card = player.getLibrary().removeFromTop(game); - if (card == null) { - break; - } - - player.moveCardToExileWithInfo(card, source.getSourceId(), exile.getName(), source.getSourceId(), game, Zone.LIBRARY); - } while (card.getCardType().contains(CardType.LAND) || card.getManaCost().convertedManaCost() >= sourceCost); - - if (card != null) { - if (player.chooseUse(outcome, "Use cascade effect on " + card.getName() + "?", game)) { - player.cast(card.getSpellAbility(), game, true); - exile.remove(card.getId()); - } - } - - while (exile.size() > 0) { - card = exile.getRandom(game); - exile.remove(card.getId()); - player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.EXILED, false, false); - } - - return true; + return CascadeAbility.applyCascade(outcome, game, source); } @Override diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CascadeTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CascadeTest.java index afdc4d69538..48124877b2f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CascadeTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CascadeTest.java @@ -1,7 +1,9 @@ package org.mage.test.cards.abilities.keywords; + import mage.constants.PhaseStep; import mage.constants.Zone; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -70,4 +72,62 @@ public class CascadeTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Arbor Elf", 0); } + + // test that player does not lose if all cards are exiled by cascade + // If you cast a spell with cascade and there are no nonland cards in your library with a converted mana + // cost that's less that that spell's converted mana cost, you'll exile your entire library. Then you'll + // randomly rearrange those cards and put them back as your library. Although you're essentially shuffling + // those cards, you're not technically doing so; abilities that trigger whenever you shuffle your library + // won't trigger. + @Test + public void testEmptyLibraryCascasde() { + + playerA.getLibrary().clear(); + + addCard(Zone.LIBRARY, playerA, "Plains", 10); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + // Ardent Plea - Enchantment {1}{W}{U} + // Exalted (Whenever a creature you control attacks alone, that creature gets +1/+1 until end of turn.) + // Cascade + addCard(Zone.HAND, playerA, "Ardent Plea"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ardent Plea"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Ardent Plea", 1); + // the 10 lands go back to library + Assert.assertEquals("The 10 lands went back to library", 10, playerA.getLibrary().size()); + Assert.assertTrue("Player A is still in game", playerA.isInGame()); + } + + @Test + public void testEmptyLibraryCascasdeNexus() { + + playerA.getLibrary().clear(); + addCard(Zone.LIBRARY, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Maelstrom Nexus"); + + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + // Aven Skirmisher - Creature {W} + addCard(Zone.HAND, playerA, "Aven Skirmisher"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Aven Skirmisher"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Maelstrom Nexus", 1); + Assert.assertTrue("Player A is still in game", playerA.isInGame()); + assertHandCount(playerA, "Aven Skirmisher", 0); + assertPermanentCount(playerA, "Aven Skirmisher", 1); + // the 2 lands go back to library + Assert.assertEquals("The 2 lands went back to library", 2, playerA.getLibrary().size()); + Assert.assertTrue("Player A is still in game", playerA.isInGame()); + + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java index 1de0c092797..5f486f7fd5a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBase.java @@ -1,10 +1,12 @@ package org.mage.test.serverside.base; -import mage.constants.PhaseStep; +import java.io.File; +import java.io.FileNotFoundException; import mage.cards.Card; import mage.cards.decks.Deck; import mage.cards.decks.importer.DeckImporterUtil; import mage.constants.MultiplayerAttackOption; +import mage.constants.PhaseStep; import mage.constants.RangeOfInfluence; import mage.filter.Filter; import mage.game.Game; @@ -16,11 +18,12 @@ import mage.players.Player; import org.junit.Assert; import org.junit.Before; import org.mage.test.player.TestPlayer; +import static org.mage.test.serverside.base.MageTestPlayerBase.TESTS_PATH; +import static org.mage.test.serverside.base.MageTestPlayerBase.activePlayer; +import static org.mage.test.serverside.base.MageTestPlayerBase.currentGame; +import static org.mage.test.serverside.base.MageTestPlayerBase.logger; import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; -import java.io.File; -import java.io.FileNotFoundException; - /** * Base class for testing single cards and effects. * @@ -217,40 +220,42 @@ public abstract class CardTestPlayerBase extends CardTestPlayerAPIImpl { if (type.equals(CardTestPlayerBase.ExpectedType.RESULT)) { String expected = getStringParam(line, 1); String actual = "draw"; - if (currentGame.getWinner().equals("Player ComputerA is the winner")) { - actual = "won"; - } else if (currentGame.getWinner().equals("Player ComputerB is the winner")) { - actual = "lost"; + switch (currentGame.getWinner()) { + case "Player ComputerA is the winner": + actual = "won"; + break; + case "Player ComputerB is the winner": + actual = "lost"; + break; } Assert.assertEquals("Game results are not equal", expected, actual); return; } + + Player player = null; + String playerName = getStringParam(line, 1); + switch (playerName) { + case "ComputerA": + player = currentGame.getPlayer(playerA.getId()); + break; + case "ComputerB": + player = currentGame.getPlayer(playerB.getId()); + break; + } + if (player == null) { + throw new IllegalArgumentException("Wrong player in 'battlefield' line, player=" + player + ", line=" + line); + } + if (type.equals(CardTestPlayerBase.ExpectedType.LIFE)) { - String player = getStringParam(line, 1); int expected = getIntParam(line, 2); - if (player.equals("ComputerA")) { - int actual = currentGame.getPlayer(playerA.getId()).getLife(); - Assert.assertEquals("Life amounts are not equal", expected, actual); - } else if (player.equals("ComputerB")) { - int actual = currentGame.getPlayer(playerB.getId()).getLife(); - Assert.assertEquals("Life amounts are not equal", expected, actual); - } else { - throw new IllegalArgumentException("Wrong player in 'life' line, player=" + player + ", line=" + line); - } + int actual = player.getLife(); + Assert.assertEquals("Life amounts are not equal", expected, actual); return; } + if (type.equals(CardTestPlayerBase.ExpectedType.BATTLEFIELD)) { - String playerName = getStringParam(line, 1); String cardName = getStringParam(line, 2); int expectedCount = getIntParam(line, 3); - Player player = null; - if (playerName.equals("ComputerA")) { - player = currentGame.getPlayer(playerA.getId()); - } else if (playerName.equals("ComputerB")) { - player = currentGame.getPlayer(playerB.getId()); - } else { - throw new IllegalArgumentException("Wrong player in 'battlefield' line, player=" + player + ", line=" + line); - } int actualCount = 0; for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) { if (permanent.getControllerId().equals(player.getId())) { @@ -262,18 +267,10 @@ public abstract class CardTestPlayerBase extends CardTestPlayerAPIImpl { Assert.assertEquals("(Battlefield) Card counts are not equal (" + cardName + ")", expectedCount, actualCount); return; } + if (type.equals(CardTestPlayerBase.ExpectedType.GRAVEYARD)) { - String playerName = getStringParam(line, 1); String cardName = getStringParam(line, 2); int expectedCount = getIntParam(line, 3); - Player player = null; - if (playerName.equals("ComputerA")) { - player = currentGame.getPlayer(playerA.getId()); - } else if (playerName.equals("ComputerB")) { - player = currentGame.getPlayer(playerB.getId()); - } else { - throw new IllegalArgumentException("Wrong player in 'graveyard' line, player=" + player + ", line=" + line); - } int actualCount = 0; for (Card card : player.getGraveyard().getCards(currentGame)) { if (card.getName().equals(cardName)) { diff --git a/Mage/src/mage/abilities/keyword/CascadeAbility.java b/Mage/src/mage/abilities/keyword/CascadeAbility.java index fdc5af6b3a3..8f689cd1eb8 100644 --- a/Mage/src/mage/abilities/keyword/CascadeAbility.java +++ b/Mage/src/mage/abilities/keyword/CascadeAbility.java @@ -28,17 +28,16 @@ package mage.abilities.keyword; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Zone; import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.stack.Spell; import mage.players.Player; @@ -94,23 +93,10 @@ public class CascadeAbility extends TriggeredAbilityImpl { public CascadeAbility copy() { return new CascadeAbility(this); } -} -// !!! Changes to the cascade effect here have to be copied to the cascadeEffect of Maelstrom Nexus card eventually. -// There is a functional copy of this effect + // moved to static method because it's called also from class {link} MaelstromNexus -class CascadeEffect extends OneShotEffect { - - public CascadeEffect() { - super(Outcome.PutCardInPlay); - } - - public CascadeEffect(CascadeEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { + public static boolean applyCascade(Outcome outcome, Game game, Ability source) { Card card; Player player = game.getPlayer(source.getControllerId()); if (player == null) { @@ -125,6 +111,7 @@ class CascadeEffect extends OneShotEffect { } player.moveCardToExileWithInfo(card, exile.getId(), exile.getName(), source.getSourceId(), game, Zone.LIBRARY); } while (player.isInGame() && card.getCardType().contains(CardType.LAND) || card.getManaCost().convertedManaCost() >= sourceCost); + player.getLibrary().reset(); if (card != null) { if (player.chooseUse(outcome, "Use cascade effect on " + card.getName() + "?", game)) { @@ -142,6 +129,55 @@ class CascadeEffect extends OneShotEffect { return true; } +} + +// !!! Changes to the cascade effect here have to be copied to the cascadeEffect of Maelstrom Nexus card eventually. +// There is a functional copy of this effect + +class CascadeEffect extends OneShotEffect { + + public CascadeEffect() { + super(Outcome.PutCardInPlay); + } + + public CascadeEffect(CascadeEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + return CascadeAbility.applyCascade(outcome, game, source); +// Card card; +// Player player = game.getPlayer(source.getControllerId()); +// if (player == null) { +// return false; +// } +// ExileZone exile = game.getExile().createZone(source.getSourceId(), player.getName() + " Cascade"); +// int sourceCost = game.getCard(source.getSourceId()).getManaCost().convertedManaCost(); +// do { +// card = player.getLibrary().getFromTop(game); +// if (card == null) { +// break; +// } +// player.moveCardToExileWithInfo(card, exile.getId(), exile.getName(), source.getSourceId(), game, Zone.LIBRARY); +// } while (player.isInGame() && card.getCardType().contains(CardType.LAND) || card.getManaCost().convertedManaCost() >= sourceCost); +// +// if (card != null) { +// if (player.chooseUse(outcome, "Use cascade effect on " + card.getName() + "?", game)) { +// if(player.cast(card.getSpellAbility(), game, true)){ +// exile.remove(card.getId()); +// } +// } +// } +// +// while (exile.size() > 0) { +// card = exile.getRandom(game); +// exile.remove(card.getId()); +// player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.EXILED, false, false); +// } +// +// return true; + } @Override public CascadeEffect copy() { @@ -149,3 +185,5 @@ class CascadeEffect extends OneShotEffect { } } + +