From a5fb925eef1a620e4b756f6998b4bc5315e6e234 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 29 May 2015 10:24:16 +0200 Subject: [PATCH 001/102] * Dack Fayden - Fixed that the controll of Artifacts taken under control by Dack Faden wrongly also change with the control of Dack Fayden. --- Mage.Sets/src/mage/sets/conspiracy/DackFayden.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/conspiracy/DackFayden.java b/Mage.Sets/src/mage/sets/conspiracy/DackFayden.java index 98a7b8650a0..0ad44a2f54a 100644 --- a/Mage.Sets/src/mage/sets/conspiracy/DackFayden.java +++ b/Mage.Sets/src/mage/sets/conspiracy/DackFayden.java @@ -84,7 +84,7 @@ public class DackFayden extends CardImpl { this.addAbility(ability); // -2: Gain control of target artifact. - effect = new GainControlTargetEffect(Duration.EndOfGame); + effect = new GainControlTargetEffect(Duration.EndOfGame, true); effect.setText("Gain control of target artifact"); ability = new LoyaltyAbility(effect, -2); ability.addTarget(new TargetArtifactPermanent()); From 66acff81b9c9e76b817b69678b33578baa81413b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 29 May 2015 12:46:34 +0200 Subject: [PATCH 002/102] Removed unused and redundant code from test player base classes. --- .../mage/test/multiplayer/PrimordialTest.java | 2 +- .../base/CardTestMultiPlayerBase.java | 271 +--------------- .../serverside/base/CardTestPlayerBase.java | 307 +----------------- .../base/impl/CardTestPlayerAPIImpl.java | 158 ++++++++- 4 files changed, 167 insertions(+), 571 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java index 6cd03426854..fec0313fc09 100644 --- a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PrimordialTest.java @@ -29,7 +29,6 @@ package org.mage.test.multiplayer; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestMultiPlayerBase; @@ -65,6 +64,7 @@ public class PrimordialTest extends CardTestMultiPlayerBase { assertPermanentCount(playerA, "Walking Corpse", 0); assertPermanentCount(playerA, "Pillarfield Ox", 1); assertGraveyardCount(playerC, "Walking Corpse", 1); + assertGraveyardCount(playerD, "Pillarfield Ox", 0); } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestMultiPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestMultiPlayerBase.java index e891527b51c..36ededfe4a3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestMultiPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestMultiPlayerBase.java @@ -1,18 +1,14 @@ package org.mage.test.serverside.base; -import mage.constants.PhaseStep; import mage.cards.Card; import mage.cards.decks.Deck; import mage.cards.decks.importer.DeckImporterUtil; import mage.constants.MultiplayerAttackOption; import mage.constants.RangeOfInfluence; -import mage.filter.Filter; import mage.game.*; import mage.game.permanent.Permanent; import mage.players.Player; import org.junit.Assert; -import org.junit.Before; -import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; import java.io.File; @@ -26,274 +22,15 @@ import java.io.FileNotFoundException; */ public abstract class CardTestMultiPlayerBase extends CardTestPlayerAPIImpl { - protected enum ExpectedType { - TURN_NUMBER, - RESULT, - LIFE, - BATTLEFIELD, - GRAVEYARD, - UNKNOWN - } - - protected GameOptions gameOptions; - - @Before - public void reset() throws GameException, FileNotFoundException { - if (currentGame != null) { - logger.debug("Resetting previous game and creating new one!"); - currentGame = null; - System.gc(); - } - + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { Game game = new FreeForAll(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20); playerA = createPlayer(game, playerA, "PlayerA"); playerB = createPlayer(game, playerB, "PlayerB"); playerC = createPlayer(game, playerC, "PlayerC"); playerD = createPlayer(game, playerD, "PlayerD"); - - activePlayer = playerA; - currentGame = game; - - stopOnTurn = 2; - stopAtStep = PhaseStep.UNTAP; - - for (Player player : currentGame.getPlayers().values()) { - TestPlayer testPlayer = (TestPlayer)player; - getCommands(testPlayer).clear(); - getLibraryCards(testPlayer).clear(); - getHandCards(testPlayer).clear(); - getBattlefieldCards(testPlayer).clear(); - getGraveCards(testPlayer).clear(); - } - - gameOptions = new GameOptions(); - } - - public void load(String path) throws FileNotFoundException, GameException { - String cardPath = TESTS_PATH + path; - File checkFile = new File(cardPath); - if (!checkFile.exists()) { - throw new FileNotFoundException("Couldn't find test file: " + cardPath); - } - if (checkFile.isDirectory()) { - throw new FileNotFoundException("Couldn't find test file: " + cardPath + ". It is directory."); - } - - if (currentGame != null) { - logger.debug("Resetting previous game and creating new one!"); - currentGame = null; - System.gc(); - } - - Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, 0, 20); - - playerA = createNewPlayer("ComputerA"); - playerA.setTestMode(true); - - Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - - if (deck.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); - } - game.addPlayer(playerA, deck); - game.loadCards(deck.getCards(), playerA.getId()); - - playerB = createNewPlayer("ComputerB"); - playerB.setTestMode(true); - Deck deck2 = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - if (deck2.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size()); - } - game.addPlayer(playerB, deck2); - game.loadCards(deck2.getCards(), playerB.getId()); - - parseScenario(cardPath); - - activePlayer = playerA; - currentGame = game; - } - - /** - * Starts testing card by starting current game. - * - * @throws IllegalStateException In case game wasn't created previously. Use {@link #load} method to initialize the game. - */ - public void execute() throws IllegalStateException { - if (currentGame == null || activePlayer == null) { - throw new IllegalStateException("Game is not initialized. Use load method to load a test case and initialize a game."); - } - - for (Player player : currentGame.getPlayers().values()) { - TestPlayer testPlayer = (TestPlayer)player; - currentGame.cheat(player.getId(), getCommands(testPlayer)); - currentGame.cheat(player.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer), - getBattlefieldCards(testPlayer), getGraveCards(testPlayer)); - } - - boolean testMode = true; - long t1 = System.nanoTime(); - - gameOptions.testMode = true; - gameOptions.stopOnTurn = stopOnTurn; - gameOptions.stopAtStep = stopAtStep; - currentGame.start(activePlayer.getId(), gameOptions); - long t2 = System.nanoTime(); - logger.debug("Winner: " + currentGame.getWinner()); - logger.info("Test has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms"); - - assertTheResults(); - } - - /** - * Assert expected and actual results. - */ - private void assertTheResults() { - logger.debug("Matching expected results:"); - for (String line : expectedResults) { - boolean ok = false; - try { - ExpectedType type = getExpectedType(line); - if (type.equals(CardTestMultiPlayerBase.ExpectedType.UNKNOWN)) { - throw new AssertionError("Unknown expected type, check the line in $expected section=" + line); - } - parseType(type, line); - ok = true; - } finally { - logger.info(" " + line + " - " + (ok ? "OK" : "ERROR")); - } - } - } - - private ExpectedType getExpectedType(String line) { - if (line.startsWith("turn:")) { - return CardTestMultiPlayerBase.ExpectedType.TURN_NUMBER; - } - if (line.startsWith("result:")) { - return CardTestMultiPlayerBase.ExpectedType.RESULT; - } - if (line.startsWith("life:")) { - return CardTestMultiPlayerBase.ExpectedType.LIFE; - } - if (line.startsWith("battlefield:")) { - return CardTestMultiPlayerBase.ExpectedType.BATTLEFIELD; - } - if (line.startsWith("graveyard:")) { - return CardTestMultiPlayerBase.ExpectedType.GRAVEYARD; - } - return CardTestMultiPlayerBase.ExpectedType.UNKNOWN; - } - - private void parseType(ExpectedType type, String line) { - if (type.equals(CardTestMultiPlayerBase.ExpectedType.TURN_NUMBER)) { - int turn = getIntParam(line, 1); - Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum()); - return; - } - if (type.equals(CardTestMultiPlayerBase.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"; - } - Assert.assertEquals("Game results are not equal", expected, actual); - return; - } - if (type.equals(CardTestMultiPlayerBase.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); - } - return; - } - if (type.equals(CardTestMultiPlayerBase.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())) { - if (permanent.getName().equals(cardName)) { - actualCount++; - } - } - } - Assert.assertEquals("(Battlefield) Card counts are not equal (" + cardName + ")", expectedCount, actualCount); - return; - } - if (type.equals(CardTestMultiPlayerBase.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)) { - actualCount++; - } - } - Assert.assertEquals("(Graveyard) Card counts are not equal (" + cardName + ")", expectedCount, actualCount); - } - } - - private int getIntParam(String line, int index) { - String[] params = line.split(":"); - if (index > params.length - 1) { - throw new IllegalArgumentException("Not correct line: " + line); - } - return Integer.parseInt(params[index]); - } - - private String getStringParam(String line, int index) { - String[] params = line.split(":"); - if (index > params.length - 1) { - throw new IllegalArgumentException("Not correct line: " + line); - } - return params[index]; - } - - protected void checkPermanentPT(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) { - if (currentGame == null) { - throw new IllegalStateException("Current game is null"); - } - if (scope.equals(Filter.ComparisonScope.All)) { - throw new UnsupportedOperationException("ComparisonScope.All is not implemented."); - } - - for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) { - if (permanent.getName().equals(cardName)) { - Assert.assertEquals("Power is not the same", power, permanent.getPower().getValue()); - Assert.assertEquals("Toughness is not the same", toughness, permanent.getToughness().getValue()); - break; - } - } - } - - protected void skipInitShuffling() { - gameOptions.skipInitShuffling = true; + return game; } + } 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 68c08699bce..e3435c3d3f4 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,27 +1,11 @@ package org.mage.test.serverside.base; -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; import mage.game.GameException; -import mage.game.GameOptions; import mage.game.TwoPlayerDuel; -import mage.game.permanent.Permanent; -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; /** @@ -30,293 +14,14 @@ import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; * @author ayratn */ public abstract class CardTestPlayerBase extends CardTestPlayerAPIImpl { - - public static final String NO_TARGET = "NO_TARGET"; - - protected enum ExpectedType { - TURN_NUMBER, - RESULT, - LIFE, - BATTLEFIELD, - GRAVEYARD, - UNKNOWN - } - - protected GameOptions gameOptions; - - public CardTestPlayerBase() { - } - + @Override - protected TestPlayer createNewPlayer(String playerName) { - return createPlayer(playerName); - } - - @Before - public void reset() throws GameException, FileNotFoundException { - if (currentGame != null) { - logger.debug("Resetting previous game and creating new one!"); - currentGame = null; - System.gc(); - } - + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20); - playerA = createNewPlayer("PlayerA"); - playerA.setTestMode(true); - logger.debug("Loading deck..."); - Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - logger.debug("Done!"); - if (deck.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); - } - game.loadCards(deck.getCards(), playerA.getId()); - game.addPlayer(playerA, deck); + playerA = createPlayer(game, playerA, "PlayerA"); + playerB = createPlayer(game, playerB, "PlayerB"); + return game; + } - playerB = createNewPlayer("PlayerB"); - playerB.setTestMode(true); - Deck deck2 = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - if (deck2.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size()); - } - game.loadCards(deck2.getCards(), playerB.getId()); - game.addPlayer(playerB, deck2); - activePlayer = playerA; - currentGame = game; - - stopOnTurn = 2; - stopAtStep = PhaseStep.UNTAP; - - for (Player player : currentGame.getPlayers().values()) { - TestPlayer testPlayer = (TestPlayer)player; - getCommands(testPlayer).clear(); - getLibraryCards(testPlayer).clear(); - getHandCards(testPlayer).clear(); - getBattlefieldCards(testPlayer).clear(); - getGraveCards(testPlayer).clear(); - } - - gameOptions = new GameOptions(); - } - - public void load(String path) throws FileNotFoundException, GameException { - String cardPath = TESTS_PATH + path; - File checkFile = new File(cardPath); - if (!checkFile.exists()) { - throw new FileNotFoundException("Couldn't find test file: " + cardPath); - } - if (checkFile.isDirectory()) { - throw new FileNotFoundException("Couldn't find test file: " + cardPath + ". It is directory."); - } - - if (currentGame != null) { - logger.debug("Resetting previous game and creating new one!"); - currentGame = null; - System.gc(); - } - - Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL, 0, 20); - - playerA = createNewPlayer("ComputerA"); - playerA.setTestMode(true); - - Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - - if (deck.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); - } - game.addPlayer(playerA, deck); - game.loadCards(deck.getCards(), playerA.getId()); - - playerB = createNewPlayer("ComputerB"); - playerB.setTestMode(true); - Deck deck2 = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); - if (deck2.getCards().size() < 40) { - throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck2.getCards().size()); - } - game.addPlayer(playerB, deck2); - game.loadCards(deck2.getCards(), playerB.getId()); - - parseScenario(cardPath); - - activePlayer = playerA; - currentGame = game; - } - - /** - * Starts testing card by starting current game. - * - * @throws IllegalStateException In case game wasn't created previously. Use {@link #load} method to initialize the game. - */ - public void execute() throws IllegalStateException { - if (currentGame == null || activePlayer == null) { - throw new IllegalStateException("Game is not initialized. Use load method to load a test case and initialize a game."); - } - - for (Player player : currentGame.getPlayers().values()) { - TestPlayer testPlayer = (TestPlayer)player; - currentGame.cheat(player.getId(), getCommands(testPlayer)); - currentGame.cheat(player.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer), - getBattlefieldCards(testPlayer), getGraveCards(testPlayer)); - } - - boolean testMode = true; - long t1 = System.nanoTime(); - - gameOptions.testMode = true; - gameOptions.stopOnTurn = stopOnTurn; - gameOptions.stopAtStep = stopAtStep; - currentGame.start(activePlayer.getId(), gameOptions); - long t2 = System.nanoTime(); - logger.debug("Winner: " + currentGame.getWinner()); - logger.info("Test has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms"); - - assertTheResults(); - } - - /** - * Assert expected and actual results. - */ - private void assertTheResults() { - logger.debug("Matching expected results:"); - for (String line : expectedResults) { - boolean ok = false; - try { - ExpectedType type = getExpectedType(line); - if (type.equals(CardTestPlayerBase.ExpectedType.UNKNOWN)) { - throw new AssertionError("Unknown expected type, check the line in $expected section=" + line); - } - parseType(type, line); - ok = true; - } finally { - logger.info(" " + line + " - " + (ok ? "OK" : "ERROR")); - } - } - } - - private ExpectedType getExpectedType(String line) { - if (line.startsWith("turn:")) { - return CardTestPlayerBase.ExpectedType.TURN_NUMBER; - } - if (line.startsWith("result:")) { - return CardTestPlayerBase.ExpectedType.RESULT; - } - if (line.startsWith("life:")) { - return CardTestPlayerBase.ExpectedType.LIFE; - } - if (line.startsWith("battlefield:")) { - return CardTestPlayerBase.ExpectedType.BATTLEFIELD; - } - if (line.startsWith("graveyard:")) { - return CardTestPlayerBase.ExpectedType.GRAVEYARD; - } - return CardTestPlayerBase.ExpectedType.UNKNOWN; - } - - private void parseType(ExpectedType type, String line) { - if (type.equals(CardTestPlayerBase.ExpectedType.TURN_NUMBER)) { - int turn = getIntParam(line, 1); - Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum()); - return; - } - if (type.equals(CardTestPlayerBase.ExpectedType.RESULT)) { - String expected = getStringParam(line, 1); - String actual = "draw"; - 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)) { - int expected = getIntParam(line, 2); - int actual = player.getLife(); - Assert.assertEquals("Life amounts are not equal", expected, actual); - return; - } - - if (type.equals(CardTestPlayerBase.ExpectedType.BATTLEFIELD)) { - String cardName = getStringParam(line, 2); - int expectedCount = getIntParam(line, 3); - int actualCount = 0; - for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) { - if (permanent.getControllerId().equals(player.getId())) { - if (permanent.getName().equals(cardName)) { - actualCount++; - } - } - } - Assert.assertEquals("(Battlefield) Card counts are not equal (" + cardName + ")", expectedCount, actualCount); - return; - } - - if (type.equals(CardTestPlayerBase.ExpectedType.GRAVEYARD)) { - String cardName = getStringParam(line, 2); - int expectedCount = getIntParam(line, 3); - int actualCount = 0; - for (Card card : player.getGraveyard().getCards(currentGame)) { - if (card.getName().equals(cardName)) { - actualCount++; - } - } - Assert.assertEquals("(Graveyard) Card counts are not equal (" + cardName + ")", expectedCount, actualCount); - } - } - - private int getIntParam(String line, int index) { - String[] params = line.split(":"); - if (index > params.length - 1) { - throw new IllegalArgumentException("Not correct line: " + line); - } - return Integer.parseInt(params[index]); - } - - private String getStringParam(String line, int index) { - String[] params = line.split(":"); - if (index > params.length - 1) { - throw new IllegalArgumentException("Not correct line: " + line); - } - return params[index]; - } - - protected void checkPermanentPT(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) { - if (currentGame == null) { - throw new IllegalStateException("Current game is null"); - } - if (scope.equals(Filter.ComparisonScope.All)) { - throw new UnsupportedOperationException("ComparisonScope.All is not implemented."); - } - - for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) { - if (permanent.getName().equals(cardName)) { - Assert.assertEquals("Power is not the same", power, permanent.getPower().getValue()); - Assert.assertEquals("Toughness is not the same", toughness, permanent.getToughness().getValue()); - break; - } - } - } - - protected void skipInitShuffling() { - gameOptions.skipInitShuffling = true; - } } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 1a3774626e3..a19f445bce5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -1,5 +1,6 @@ package org.mage.test.serverside.base.impl; +import java.io.FileNotFoundException; import mage.abilities.Ability; import mage.cards.Card; import mage.cards.decks.Deck; @@ -28,6 +29,8 @@ import org.mage.test.serverside.base.MageTestPlayerBase; import java.util.List; import java.util.UUID; +import mage.game.GameOptions; +import org.junit.Before; /** * API for test initialization and asserting the test results. @@ -36,6 +39,20 @@ import java.util.UUID; */ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implements CardTestAPI { + // Defines the constant if for activate ability is not target but a ability on the stack to define + public static final String NO_TARGET = "NO_TARGET"; + + protected GameOptions gameOptions; + + protected enum ExpectedType { + TURN_NUMBER, + RESULT, + LIFE, + BATTLEFIELD, + GRAVEYARD, + UNKNOWN + } + static { // CardScanner.scanned = true; CardScanner.scan(); @@ -83,7 +100,36 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement removeAllCardsFromLibrary(playerB); addCard(Zone.LIBRARY, playerB, "Plains", 10); } + + @Before + public void reset() throws GameException, FileNotFoundException { + if (currentGame != null) { + logger.debug("Resetting previous game and creating new one!"); + currentGame = null; + System.gc(); + } + currentGame = createNewGameAndPlayers(); + + activePlayer = playerA; + + stopOnTurn = 2; + stopAtStep = PhaseStep.UNTAP; + + for (Player player : currentGame.getPlayers().values()) { + TestPlayer testPlayer = (TestPlayer)player; + getCommands(testPlayer).clear(); + getLibraryCards(testPlayer).clear(); + getHandCards(testPlayer).clear(); + getBattlefieldCards(testPlayer).clear(); + getGraveCards(testPlayer).clear(); + } + + gameOptions = new GameOptions(); + } + + abstract protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException; + protected TestPlayer createPlayer(Game game, TestPlayer player, String name) throws GameException { player = createNewPlayer(name); player.setTestMode(true); @@ -93,16 +139,68 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement if (deck.getCards().size() < 40) { throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); } - game.addPlayer(player, deck); game.loadCards(deck.getCards(), player.getId()); + game.addPlayer(player, deck); return player; } + /** + * Starts testing card by starting current game. + * + * @throws IllegalStateException In case game wasn't created previously. Use {@link #load} method to initialize the game. + */ + public void execute() throws IllegalStateException { + if (currentGame == null || activePlayer == null) { + throw new IllegalStateException("Game is not initialized. Use load method to load a test case and initialize a game."); + } + + for (Player player : currentGame.getPlayers().values()) { + TestPlayer testPlayer = (TestPlayer)player; + currentGame.cheat(player.getId(), getCommands(testPlayer)); + currentGame.cheat(player.getId(), getLibraryCards(testPlayer), getHandCards(testPlayer), + getBattlefieldCards(testPlayer), getGraveCards(testPlayer)); + } + + long t1 = System.nanoTime(); + + gameOptions.testMode = true; + gameOptions.stopOnTurn = stopOnTurn; + gameOptions.stopAtStep = stopAtStep; + currentGame.start(activePlayer.getId(), gameOptions); + long t2 = System.nanoTime(); + logger.debug("Winner: " + currentGame.getWinner()); + logger.info("Test has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms"); + + } + + + protected TestPlayer createNewPlayer(String playerName) { return createPlayer(playerName); } - + + protected Player getPlayerFromName(String playerName, String line) { + Player player = null; + switch (playerName) { + case "ComputerA": + player = currentGame.getPlayer(playerA.getId()); + break; + case "ComputerB": + player = currentGame.getPlayer(playerB.getId()); + break; + case "ComputerC": + player = currentGame.getPlayer(playerC.getId()); + break; + case "ComputerD": + player = currentGame.getPlayer(playerD.getId()); + break; + default: + throw new IllegalArgumentException("Wrong player in 'battlefield' line, player=" + player + ", line=" + line); + } + return player; + } + /** * Removes all cards from player's library from the game. * Usually this should be used once before initialization to form the library in certain order. @@ -897,5 +995,61 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public void addTarget(TestPlayer player, TestPlayer targetPlayer) { player.addTarget("targetPlayer="+targetPlayer.getName()); } + + protected void skipInitShuffling() { + gameOptions.skipInitShuffling = true; + } + + protected ExpectedType getExpectedType(String line) { + if (line.startsWith("turn:")) { + return ExpectedType.TURN_NUMBER; + } + if (line.startsWith("result:")) { + return ExpectedType.RESULT; + } + if (line.startsWith("life:")) { + return ExpectedType.LIFE; + } + if (line.startsWith("battlefield:")) { + return ExpectedType.BATTLEFIELD; + } + if (line.startsWith("graveyard:")) { + return ExpectedType.GRAVEYARD; + } + return ExpectedType.UNKNOWN; + } + + protected String getStringParam(String line, int index) { + String[] params = line.split(":"); + if (index > params.length - 1) { + throw new IllegalArgumentException("Not correct line: " + line); + } + return params[index]; + } + + protected void checkPermanentPT(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) { + if (currentGame == null) { + throw new IllegalStateException("Current game is null"); + } + if (scope.equals(Filter.ComparisonScope.All)) { + throw new UnsupportedOperationException("ComparisonScope.All is not implemented."); + } + for (Permanent permanent : currentGame.getBattlefield().getAllActivePermanents(player.getId())) { + if (permanent.getName().equals(cardName)) { + Assert.assertEquals("Power is not the same", power, permanent.getPower().getValue()); + Assert.assertEquals("Toughness is not the same", toughness, permanent.getToughness().getValue()); + break; + } + } + } + + protected int getIntParam(String line, int index) { + String[] params = line.split(":"); + if (index > params.length - 1) { + throw new IllegalArgumentException("Not correct line: " + line); + } + return Integer.parseInt(params[index]); + } + } From 8783a633a19b64360d9329a4ae78de29f33b9c8b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 29 May 2015 15:07:16 +0200 Subject: [PATCH 003/102] Added commander duel test base class. --- Mage.Tests/CommanderDuel.dck | 73 +++++++++++++++++++ .../commander/duel/CastCommanderTest.java | 56 ++++++++++++++ .../base/CardTestCommanderDuelBase.java | 54 ++++++++++++++ .../base/impl/CardTestPlayerAPIImpl.java | 7 +- 4 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 Mage.Tests/CommanderDuel.dck create mode 100644 Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommanderDuelBase.java diff --git a/Mage.Tests/CommanderDuel.dck b/Mage.Tests/CommanderDuel.dck new file mode 100644 index 00000000000..2999d2ef2b5 --- /dev/null +++ b/Mage.Tests/CommanderDuel.dck @@ -0,0 +1,73 @@ +NAME:Sworn to Darkness +1 [C14:54] Commander's Sphere +1 [C14:307] Polluted Mire +1 [C14:59] Arcane Lighthouse +1 [C14:58] Unstable Obelisk +1 [C14:149] Morkrut Banshee +1 [C14:148] Magus of the Coffers +1 [C14:147] Liliana's Reaver +1 [C14:146] Gray Merchant of Asphodel +1 [C14:145] Grave Titan +1 [C14:144] Evernight Shade +1 [C14:143] Dregs of Sorrow +1 [C14:142] Dread Return +1 [C14:141] Drana, Kalastria Bloodchief +1 [C14:140] Disciple of Bolas +1 [C14:23] Ghoulcaller Gisa +1 [C14:22] Flesh Carver +1 [C14:21] Demon of Wailing Agonies +1 [C14:61] Myriad Landscape +1 [C14:29] Raving Dead +1 [C14:28] Overseer of the Damned +1 [C14:26] Necromantic Selection +1 [C14:25] Malicious Affliction +1 [C14:24] Infernal Offering +1 [C14:159] Reaper from the Abyss +1 [C14:235] Charcoal Diamond +1 [C14:158] Read the Bones +1 [C14:157] Promise of Power +1 [C14:156] Profane Command +1 [C14:232] Burnished Hart +1 [C14:155] Pontiff of Blight +1 [C14:154] Phyrexian Gargantua +1 [C14:275] Swiftfoot Boots +1 [C14:153] Pestilence Demon +1 [C14:152] Nekrataal +1 [C14:151] Nantuko Shade +1 [C14:150] Mutilate +1 [C14:270] Sol Ring +1 [C14:31] Wake the Dead +1 [C14:30] Spoils of Blood +8 [C14:329] Swamp +8 [C14:328] Swamp +8 [C14:327] Swamp +8 [C14:326] Swamp +1 [C14:169] Victimize +1 [C14:168] Vampire Hexmage +1 [C14:245] Lashwrithe +1 [C14:167] Tragic Slip +1 [C14:288] Crypt of Agadeem +1 [C14:243] Jet Medallion +1 [C14:166] Tendrils of Corruption +1 [C14:165] Syphon Mind +1 [C14:164] Sudden Spoiling +1 [C14:285] Bojuka Bog +1 [C14:163] Skirsdag High Priest +1 [C14:284] Barren Moor +1 [C14:162] Skeletal Scrying +1 [C14:282] Worn Powerstone +1 [C14:161] Sign in Blood +1 [C14:160] Shriekmaw +1 [C14:139] Crypt Ghast +1 [C14:138] Butcher of Malakir +1 [C14:137] Bloodgift Demon +1 [C14:136] Black Sun's Zenith +1 [C14:135] Bad Moon +1 [C14:134] Annihilate +1 [C14:133] AEther Snap +1 [C14:298] Ghost Quarter +1 [C14:132] Abyssal Persecutor +1 [C14:250] Mind Stone +1 [C14:294] Everglades +1 [C14:170] Xathrid Demon +SB: 1 [C14:27] Ob Nixilis of the Black Oath diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java new file mode 100644 index 00000000000..4a948571b9e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java @@ -0,0 +1,56 @@ +/* + * 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 mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Ignore; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * + * @author LevelX2 + */ +public class CastCommanderTest extends CardTestCommanderDuelBase { + @Test + @Ignore + public void testFirstAbility() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ob Nixilis of the Black Oath"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 40); + assertLife(playerB, 40); + + assertPermanentCount(playerA, "Ob Nixilis of the Black Oath", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommanderDuelBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommanderDuelBase.java new file mode 100644 index 00000000000..c31449aa0e8 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestCommanderDuelBase.java @@ -0,0 +1,54 @@ +/* + * 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.serverside.base; + +import java.io.FileNotFoundException; +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.CommanderDuel; +import mage.game.Game; +import mage.game.GameException; +import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; + +/** + * + * @author LevelX2 + */ + +public abstract class CardTestCommanderDuelBase extends CardTestPlayerAPIImpl { + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + Game game = new CommanderDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 40); + + playerA = createPlayer(game, playerA, "PlayerA","CommanderDuel.dck"); + playerB = createPlayer(game, playerB, "PlayerB","CommanderDuel.dck"); + return game; + } + +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index a19f445bce5..771de0af82d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -131,15 +131,20 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement abstract protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException; protected TestPlayer createPlayer(Game game, TestPlayer player, String name) throws GameException { + return createPlayer(game, player, name, "RB Aggro.dck"); + } + + protected TestPlayer createPlayer(Game game, TestPlayer player, String name, String deckName) throws GameException { player = createNewPlayer(name); player.setTestMode(true); logger.debug("Loading deck..."); - Deck deck = Deck.load(DeckImporterUtil.importDeck("RB Aggro.dck"), false, false); + Deck deck = Deck.load(DeckImporterUtil.importDeck(deckName), false, false); logger.debug("Done!"); if (deck.getCards().size() < 40) { throw new IllegalArgumentException("Couldn't load deck, deck size=" + deck.getCards().size()); } game.loadCards(deck.getCards(), player.getId()); + game.loadCards(deck.getSideboard(), player.getId()); game.addPlayer(player, deck); return player; From 4f4ade61d5fe0770b530216fdbfe2ffc6d4a2017 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 29 May 2015 17:29:14 +0200 Subject: [PATCH 004/102] Fixed commander cast test. --- .../java/org/mage/test/commander/duel/CastCommanderTest.java | 1 - Mage/src/mage/game/GameCommanderImpl.java | 3 ++- Mage/src/mage/players/PlayerImpl.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java index 4a948571b9e..4d5a3a7c868 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java @@ -39,7 +39,6 @@ import org.mage.test.serverside.base.CardTestCommanderDuelBase; */ public class CastCommanderTest extends CardTestCommanderDuelBase { @Test - @Ignore public void testFirstAbility() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); diff --git a/Mage/src/mage/game/GameCommanderImpl.java b/Mage/src/mage/game/GameCommanderImpl.java index b5e4fae424f..42d3ac77ef8 100644 --- a/Mage/src/mage/game/GameCommanderImpl.java +++ b/Mage/src/mage/game/GameCommanderImpl.java @@ -83,9 +83,10 @@ public abstract class GameCommanderImpl extends GameImpl { if (player != null){ if (player.getSideboard().size() > 0){ Card commander = getCard((UUID)player.getSideboard().toArray()[0]); - if (commander != null) { + if (commander != null) { player.setCommanderId(commander.getId()); commander.moveToZone(Zone.COMMAND, null, this, true); + commander.getAbilities().setControllerId(player.getId()); ability.addEffect(new CommanderReplacementEffect(commander.getId(), alsoHand, alsoLibrary)); ability.addEffect(new CommanderCostModification(commander.getId())); ability.addEffect(new CommanderManaReplacementEffect(player.getId(), CardUtil.getColorIdentity(commander))); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index f5cc42cb979..ef6a0004790 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2454,7 +2454,7 @@ public abstract class PlayerImpl implements Player, Serializable { for (CommandObject commandObject : game.getState().getCommand()) { for (ActivatedAbility ability : commandObject.getAbilities().getActivatedAbilities(Zone.COMMAND)) { if (ability.getControllerId().equals(getId()) - && ability.getAbilityType().equals(AbilityType.ACTIVATED) + && ability instanceof ActivatedAbility && canPlay(ability, availableMana, game.getObject(ability.getSourceId()), game)) { playableActivated.put(ability.toString(), ability); } From 9d55dd45f5621ad80d3fd9cf5c4ead850786bb87 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 29 May 2015 17:29:32 +0200 Subject: [PATCH 005/102] Reworked Wave of Vitrol. --- .../sets/commander2014/WaveOfVitriol.java | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/Mage.Sets/src/mage/sets/commander2014/WaveOfVitriol.java b/Mage.Sets/src/mage/sets/commander2014/WaveOfVitriol.java index de2c61fe28c..bf1405c76a0 100644 --- a/Mage.Sets/src/mage/sets/commander2014/WaveOfVitriol.java +++ b/Mage.Sets/src/mage/sets/commander2014/WaveOfVitriol.java @@ -27,6 +27,8 @@ */ package mage.sets.commander2014; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; @@ -57,7 +59,6 @@ public class WaveOfVitriol extends CardImpl { super(ownerId, 51, "Wave of Vitriol", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{5}{G}{G}"); this.expansionSetCode = "C14"; - // Each player sacrifices all artifacts, enchantments, and nonbasic lands he or she controls. For each land sacrificed this way, its controller may search his or her library for a basic land card and put it onto the battlefield tapped. Then each player who searched his or her library this way shuffles it. this.getSpellAbility().addEffect(new WaveOfVitriolEffect()); @@ -106,6 +107,7 @@ class WaveOfVitriolEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { + Map sacrificedLands = new HashMap<>(); for(UUID playerId: controller.getInRange()) { Player player = game.getPlayer(playerId); if (player != null) { @@ -115,21 +117,33 @@ class WaveOfVitriolEffect extends OneShotEffect { count++; } } - game.getState().handleSimultaneousEvent(game); - if (count > 0 && player.chooseUse(Outcome.PutLandInPlay, "Search your library for up to " + count + " basic lands?", game)) { - Target target = new TargetCardInLibrary(0,count, new FilterBasicLandCard()); - player.chooseTarget(outcome, target, source, game); - for(UUID targetId: target.getTargets()) { - Card card = game.getCard(targetId); - if (card != null) { - player.putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, source.getSourceId(), true); - } - } - player.shuffleLibrary(game); - game.getState().handleSimultaneousEvent(game); + if (count > 0) { + sacrificedLands.put(player, count); } } } + game.getState().handleSimultaneousEvent(game); + for(Map.Entry entry: sacrificedLands.entrySet()) { + if (entry.getKey().chooseUse(Outcome.PutLandInPlay, "Search your library for up to " + entry.getValue() + " basic lands?", game)) { + Target target = new TargetCardInLibrary(0, entry.getValue(), new FilterBasicLandCard()); + entry.getKey().chooseTarget(outcome, target, source, game); + for(UUID targetId: target.getTargets()) { + Card card = game.getCard(targetId); + if (card != null) { + entry.getKey().putOntoBattlefieldWithInfo(card, game, Zone.LIBRARY, source.getSourceId(), true); + } + } + entry.getKey().shuffleLibrary(game); + } else { + entry.setValue(0); + } + } + for(Map.Entry entry: sacrificedLands.entrySet()) { + if (entry.getValue() > 0) { + entry.getKey().shuffleLibrary(game); + } + } + return true; } return false; From 5a428f10ab15c3f3bc5291366b646dbdea7440fa Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 29 May 2015 18:43:21 +0200 Subject: [PATCH 006/102] * Fixed a bug that continuous effects created by copied cards (e.g. by Isochronic Scepter using Silence) were not applied correctly. --- .../src/mage/sets/magic2010/Silence.java | 13 ++-- .../test/cards/continuous/SilenceTest.java | 63 +++++++++++++++++++ .../test/cards/copy/IsochronScepterTest.java | 35 +++++++++++ .../abilities/effects/ContinuousEffects.java | 11 +++- Mage/src/mage/game/GameState.java | 1 + 5 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/continuous/SilenceTest.java diff --git a/Mage.Sets/src/mage/sets/magic2010/Silence.java b/Mage.Sets/src/mage/sets/magic2010/Silence.java index 4e406fbe936..4637cd9f97e 100644 --- a/Mage.Sets/src/mage/sets/magic2010/Silence.java +++ b/Mage.Sets/src/mage/sets/magic2010/Silence.java @@ -51,6 +51,7 @@ public class Silence extends CardImpl { super(ownerId, 31, "Silence", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{W}"); this.expansionSetCode = "M10"; + // Your opponents can't cast spells this turn. (Spells cast before this resolves are unaffected.) this.getSpellAbility().addEffect(new SilenceEffect()); } @@ -68,7 +69,7 @@ class SilenceEffect extends ContinuousRuleModifyingEffectImpl { public SilenceEffect() { super(Duration.EndOfTurn, Outcome.Benefit); - staticText = "Your opponents can't cast spells this turn"; + staticText = "Your opponents can't cast spells this turn. (Spells cast before this resolves are unaffected.)"; } public SilenceEffect(final SilenceEffect effect) { @@ -94,12 +95,14 @@ class SilenceEffect extends ContinuousRuleModifyingEffectImpl { return null; } + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.CAST_SPELL; + } + @Override public boolean applies(GameEvent event, Ability source, Game game) { - if (event.getType() == EventType.CAST_SPELL && game.getOpponents(source.getControllerId()).contains(event.getPlayerId())) { - return true; - } - return false; + return game.getOpponents(source.getControllerId()).contains(event.getPlayerId()); } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SilenceTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SilenceTest.java new file mode 100644 index 00000000000..80b18593084 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/SilenceTest.java @@ -0,0 +1,63 @@ +/* + * 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.cards.continuous; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class SilenceTest extends CardTestPlayerBase { + + @Test + public void testSilence() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.HAND, playerA, "Silence"); + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + addCard(Zone.HAND, playerB, "Silvercoat Lion", 1); + + castSpell(2, PhaseStep.UPKEEP, playerA, "Silence"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); + setStopAt(2, PhaseStep.BEGIN_COMBAT); + + execute(); + + assertGraveyardCount(playerA, "Silence", 1); + + assertHandCount(playerB, "Silvercoat Lion", 1); + assertPermanentCount(playerB, "Silvercoat Lion", 0); + } + +} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java index a5b1a67587b..71506a20563 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/IsochronScepterTest.java @@ -142,4 +142,39 @@ public class IsochronScepterTest extends CardTestPlayerBase { assertLife(playerB, 20); } + + /** + * Resolving a Silence cast from exile via Isochron Scepter during my opponent's upkeep does + * not prevent that opponent from casting spells that turn. + * + */ + + @Test + public void testSilence() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 4); + addCard(Zone.HAND, playerA, "Isochron Scepter"); + addCard(Zone.HAND, playerA, "Silence"); + + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + addCard(Zone.HAND, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Isochron Scepter"); + addTarget(playerA, "Silence"); + + activateAbility(2, PhaseStep.UPKEEP, playerA, "{2},{T}:"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Isochron Scepter", 1); + assertExileCount("Silence", 1); + + assertHandCount(playerB, "Silvercoat Lion", 1); + assertPermanentCount(playerB, "Silvercoat Lion", 0); + + } } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 0f76f9b5724..561f1facc32 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -390,7 +390,16 @@ public class ContinuousEffects implements Serializable { } private boolean checkAbilityStillExists(Ability ability, ContinuousEffect effect, GameEvent event, Game game) { - if (effect.getDuration().equals(Duration.OneUse) || effect.getDuration().equals(Duration.Custom) || ability.getSourceId() == null) { // needed for some special replacment effects (e.g. Undying) or commander replacement effect + switch(effect.getDuration()) { // effects with fixed duration don't need an object with the source ability (e.g. a silence cast with isochronic Scepter has no more a card object + case EndOfCombat: + case EndOfGame: + case EndOfStep: + case EndOfTurn: + case OneUse: + case Custom: // custom duration means the effect ends itself if needed + return true; + } + if (ability.getSourceId() == null) { // commander replacement effect return true; } MageObject object; diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index 332ead3e168..8e82a48f4ff 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -840,6 +840,7 @@ public class GameState implements Serializable, Copyable { values.clear(); zones.clear(); simultaneousEvents.clear(); + copiedCards.clear(); permanentOrderNumber = 0; } From bf598d1af2f1c512d68894ecec442722a9fa8d4a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 00:25:48 +0200 Subject: [PATCH 007/102] * Laboratory Maniac - Removed wrong checks that prevented the player to win if his life was below 1 and he drew from an empty libraray. --- .../mage/sets/innistrad/LaboratoryManiac.java | 5 +- .../src/mage/sets/timespiral/AngelsGrace.java | 2 +- .../cards/replacement/WinLoseEffectsTest.java | 51 +++++++++++++++++++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/sets/innistrad/LaboratoryManiac.java b/Mage.Sets/src/mage/sets/innistrad/LaboratoryManiac.java index a5587cda001..ee8f1a919f5 100644 --- a/Mage.Sets/src/mage/sets/innistrad/LaboratoryManiac.java +++ b/Mage.Sets/src/mage/sets/innistrad/LaboratoryManiac.java @@ -113,10 +113,7 @@ class LaboratoryManiacEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getPlayerId().equals(source.getControllerId())) { Player player = game.getPlayer(event.getPlayerId()); - if (!player.hasLost() && ( - (player.getLife() > 0 || !player.canLoseByZeroOrLessLife()) - && player.isEmptyDraw() - && player.getCounters().getCount(CounterType.POISON) < 10)) { + if (player != null && !player.hasLost() && player.isEmptyDraw()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/timespiral/AngelsGrace.java b/Mage.Sets/src/mage/sets/timespiral/AngelsGrace.java index f51413dd1f5..3a7aef912e8 100644 --- a/Mage.Sets/src/mage/sets/timespiral/AngelsGrace.java +++ b/Mage.Sets/src/mage/sets/timespiral/AngelsGrace.java @@ -53,7 +53,7 @@ public class AngelsGrace extends CardImpl { this.expansionSetCode = "TSP"; - // Split second + // Split second (As long as this spell is on the stack, players can't cast spells or activate abilities that aren't mana abilities.) this.addAbility(new SplitSecondAbility()); // You can't lose the game this turn and your opponents can't win the game this turn. Until end of turn, damage that would reduce your life total to less than 1 reduces it to 1 instead. diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java index d7ee8020536..41f230092b2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/WinLoseEffectsTest.java @@ -93,4 +93,55 @@ public class WinLoseEffectsTest extends CardTestPlayerBase { assertLife(playerB, 20); } + + @Test + public void testAngelsGrace2() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + // Instant {W} + // You can't lose the game this turn and your opponents can't win the game this turn. Until end of turn, damage that would reduce your life total to less than 1 reduces it to 1 instead. + addCard(Zone.HAND, playerA, "Angel's Grace"); + // Instant - {3}{B}{B} + // Reveal the top card of your library and put that card into your hand. You lose life equal to its converted mana cost. + // You may repeat this process any number of times. + addCard(Zone.HAND, playerA, "Ad Nauseam"); + + // Creature + // If you would draw a card while your library has no cards in it, you win the game instead. + addCard(Zone.BATTLEFIELD, playerA, "Laboratory Maniac", 1); + + skipInitShuffling(); + + playerA.getLibrary().clear(); + // Instant {U} + // Draw a card. Scry 2 + addCard(Zone.LIBRARY, playerA, "Serum Visions"); // 1 life lost + addCard(Zone.LIBRARY, playerA, "Bogardan Hellkite", 3); // 24 life lost + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel's Grace"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ad Nauseam"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); + setChoice(playerA, "Yes"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Serum Visions"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Angel's Grace", 1); + assertGraveyardCount(playerA, "Ad Nauseam", 1); + assertGraveyardCount(playerA, "Serum Visions", 1); + + Assert.assertEquals("Player A library is empty", 0 , playerA.getLibrary().size()); + + assertLife(playerA, -5); + assertLife(playerB, 20); + + Assert.assertTrue("Player A has not won but should have", playerA.hasWon()); + + } } From 6c14cfb590dbac17e3c0235b1a151d5c8ede9ab2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 00:29:46 +0200 Subject: [PATCH 008/102] * Avarice Totem - Fixed the non working control changing activated ability. --- Mage.Sets/src/mage/sets/fifthdawn/AvariceTotem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/fifthdawn/AvariceTotem.java b/Mage.Sets/src/mage/sets/fifthdawn/AvariceTotem.java index c3a165351ca..712d873c4f3 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/AvariceTotem.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/AvariceTotem.java @@ -51,7 +51,7 @@ public class AvariceTotem extends CardImpl { this.expansionSetCode = "5DN"; // {5}: Exchange control of Avarice Totem and target nonland permanent. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExchangeControlTargetEffect(Duration.EndOfGame, rule, false), new ManaCostsImpl("{5}")); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExchangeControlTargetEffect(Duration.EndOfGame, rule, true), new ManaCostsImpl("{5}")); ability.addTarget(new TargetNonlandPermanent()); this.addAbility(ability); } From 4aca9b6e94c008603c17f8066c1c60f6b03ff32f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 01:08:55 +0200 Subject: [PATCH 009/102] * Praetor's Grasp - Fixed that the exiled card could not be looked at nor played. --- .../mage/sets/newphyrexia/PraetorsGrasp.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java b/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java index eebfcb13119..82ea8bc0814 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/PraetorsGrasp.java @@ -59,11 +59,9 @@ public class PraetorsGrasp extends CardImpl { super(ownerId, 71, "Praetor's Grasp", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{B}{B}"); this.expansionSetCode = "NPH"; - // Search target opponent's library for a card and exile it face down. Then that player shuffles his or her library. You may look at and play that card for as long as it remains exiled. this.getSpellAbility().addEffect(new PraetorsGraspEffect()); this.getSpellAbility().addTarget(new TargetOpponent()); - } public PraetorsGrasp(final PraetorsGrasp card) { @@ -105,7 +103,7 @@ class PraetorsGraspEffect extends OneShotEffect { UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (card != null && exileId != null) { game.informPlayers(controller.getLogName() + " moves the searched card face down to exile"); - card.moveToExile(exileId, sourceObject.getName(), source.getSourceId(), game); + card.moveToExile(exileId, sourceObject.getIdName(), source.getSourceId(), game); card.setFaceDown(true, game); game.addEffect(new PraetorsGraspPlayEffect(card.getId()), source); game.addEffect(new PraetorsGraspRevealEffect(card.getId()), source); @@ -144,12 +142,11 @@ class PraetorsGraspPlayEffect extends AsThoughEffectImpl { } @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId.equals(cardId)) { + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (objectId.equals(cardId) && affectedControllerId.equals(source.getControllerId())) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = source.getSourceObject(game); - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject, true); - if (exileId != null && sourceObject != null && controller != null) { + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + if (exileId != null && controller != null) { ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone != null && exileZone.contains(cardId)) { if (controller.chooseUse(outcome, "Play the exiled card?", game)) { @@ -191,10 +188,10 @@ class PraetorsGraspRevealEffect extends AsThoughEffectImpl { } @Override - public boolean applies(UUID sourceId, Ability source, UUID affectedControllerId, Game game) { - if (sourceId.equals(cardId)) { + public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { + if (objectId.equals(cardId) && affectedControllerId.equals(source.getControllerId())) { MageObject sourceObject = source.getSourceObject(game); - UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject, true); + UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); if (exileId != null && sourceObject != null) { ExileZone exileZone = game.getExile().getExileZone(exileId); if (exileZone != null && exileZone.contains(cardId)) { @@ -203,7 +200,7 @@ class PraetorsGraspRevealEffect extends AsThoughEffectImpl { if (controller != null && card != null && game.getState().getZone(cardId) == Zone.EXILED) { if (controller.chooseUse(outcome, "Reveal exiled card?", game)) { Cards cards = new CardsImpl(card); - controller.lookAtCards("Exiled with " + sourceObject.getName(), cards, game); + controller.lookAtCards("Exiled with " + sourceObject.getIdName(), cards, game); } } } else { From 04d555d339fa582f970bbf1bc4f4f04ed4438585 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 01:44:47 +0200 Subject: [PATCH 010/102] * Opal Palace - Fixed that a commander casr with opal palace did not get a +1/+1 counter. --- .../mage/sets/commander2013/OpalPalace.java | 14 ++-- .../commander/duel/CastCommanderTest.java | 1 - .../test/commander/duel/OpalPalaceTest.java | 68 +++++++++++++++++++ 3 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java diff --git a/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java b/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java index 33704e9e315..4601e1a29d5 100644 --- a/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java +++ b/Mage.Sets/src/mage/sets/commander2013/OpalPalace.java @@ -64,12 +64,12 @@ public class OpalPalace extends CardImpl { super(ownerId, 310, "Opal Palace", Rarity.COMMON, new CardType[]{CardType.LAND}, ""); this.expansionSetCode = "C13"; - // {tap}: Add {1} to your mana pool. + // {T}: Add {1} to your mana pool. this.addAbility(new ColorlessManaAbility()); // {1}, {tap}: Add to your mana pool one mana of any color in your commander's color identity. If you spend this mana to cast your commander, it enters the battlefield with a number of +1/+1 counters on it equal to the number of times it's been cast from the command zone this game. Ability ability = new CommanderColorIdentityManaAbility(new GenericManaCost(1)); ability.addCost(new TapSourceCost()); - this.addAbility(ability, new OpalPalaceWatcher()); + this.addAbility(ability, new OpalPalaceWatcher(ability.getOriginalId().toString())); ability = new SimpleStaticAbility(Zone.ALL, new OpalPalaceEntersBattlefieldEffect()); ability.setRuleVisible(false); @@ -90,13 +90,17 @@ public class OpalPalace extends CardImpl { class OpalPalaceWatcher extends Watcher { public List commanderId = new ArrayList<>(); - - public OpalPalaceWatcher() { + private final String originalId; + + public OpalPalaceWatcher(String originalId) { super("ManaPaidFromOpalPalaceWatcher", WatcherScope.CARD); + this.originalId = originalId; } public OpalPalaceWatcher(final OpalPalaceWatcher watcher) { super(watcher); + this.commanderId.addAll(watcher.commanderId); + this.originalId = watcher.originalId; } @Override @@ -107,7 +111,7 @@ class OpalPalaceWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.MANA_PAYED) { - if (event.getSourceId().equals(this.getSourceId()) && event.getFlag()) { // flag indicates that mana was produced with second ability + if (event.getData() != null && event.getData().equals(originalId)) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell != null) { Card card = spell.getCard(); diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java index 4d5a3a7c868..33bbc816b54 100644 --- a/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/CastCommanderTest.java @@ -29,7 +29,6 @@ package org.mage.test.commander.duel; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestCommanderDuelBase; diff --git a/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java new file mode 100644 index 00000000000..b83ad522a16 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/commander/duel/OpalPalaceTest.java @@ -0,0 +1,68 @@ +/* + * 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 mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestCommanderDuelBase; + +/** + * + * @author LevelX2 + */ + +public class OpalPalaceTest extends CardTestCommanderDuelBase { + /** + * I cast my commander with Opal Palace's second ability and it did not receive a +1/+1 counter + * the first time it was cast (rulings say it should on the first time cast). + */ + @Test + public void testFirstAbility() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); + + // {T}: Add {1} to your mana pool. + // {1}, {T}: Add to your mana pool one mana of any color in your commander's color identity. + // If you spend this mana to cast your commander, it enters the battlefield with a number of +1/+1 counters on it + // equal to the number of times it's been cast from the command zone this game. + addCard(Zone.BATTLEFIELD, playerA, "Opal Palace", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ob Nixilis of the Black Oath"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 40); + assertLife(playerB, 40); + + assertPermanentCount(playerA, "Ob Nixilis of the Black Oath", 1); + assertCounterCount("Ob Nixilis of the Black Oath", CounterType.P1P1, 1); + } +} From fa2f7e205508a44f92d87d73d4715ae4531dd1b5 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 02:00:21 +0200 Subject: [PATCH 011/102] Grave Pact, Dictate of Erebos, Butcher of Malakir, Agent of The Fates, Tribute to the Wild and Yawning Fissure - Fixed that the selection for the object to sacrifice was handled targeted. --- .../src/mage/sets/ninthedition/GravePact.java | 29 +++++++++++++------ .../common/SacrificeOpponentsEffect.java | 9 ++---- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Mage.Sets/src/mage/sets/ninthedition/GravePact.java b/Mage.Sets/src/mage/sets/ninthedition/GravePact.java index 502ff727012..08c949d9b2e 100644 --- a/Mage.Sets/src/mage/sets/ninthedition/GravePact.java +++ b/Mage.Sets/src/mage/sets/ninthedition/GravePact.java @@ -27,6 +27,8 @@ */ package mage.sets.ninthedition; +import java.util.ArrayList; +import java.util.List; import java.util.UUID; import mage.constants.CardType; import mage.constants.Outcome; @@ -41,7 +43,6 @@ import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.Target; import mage.target.common.TargetControlledCreaturePermanent; /** @@ -121,18 +122,28 @@ class GravePactEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - for (UUID playerId : game.getPlayerList()) { - if (!playerId.equals(source.getControllerId())) { + List perms = new ArrayList<>(); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId : controller.getInRange()) { Player player = game.getPlayer(playerId); - Target target = new TargetControlledCreaturePermanent(); - if (player != null && player.choose(Outcome.Sacrifice, target, source.getSourceId(), game)) { - Permanent permanent = game.getPermanent(target.getFirstTarget()); - if (permanent != null) { - permanent.sacrifice(source.getSourceId(), game); + if (player != null && !playerId.equals(source.getControllerId())) { + TargetControlledCreaturePermanent target = new TargetControlledCreaturePermanent(); + target.setNotTarget(true); + if (target.canChoose(player.getId(), game)) { + player.chooseTarget(Outcome.Sacrifice, target, source, game); + perms.addAll(target.getTargets()); } } } + for (UUID permID : perms) { + Permanent permanent = game.getPermanent(permID); + if (permanent != null) { + permanent.sacrifice(source.getSourceId(), game); + } + } + return true; } - return false; + return false; } } diff --git a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java index 2320b261986..6edd6c83449 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeOpponentsEffect.java @@ -87,14 +87,11 @@ public class SacrificeOpponentsEffect extends OneShotEffect { filter.add(new ControllerPredicate(TargetController.YOU)); for (UUID playerId : game.getOpponents(source.getControllerId())) { Player player = game.getPlayer(playerId); - if (player != null) { - + if (player != null) { int numTargets = Math.min(amount.calculate(game, source, this), game.getBattlefield().countAll(filter, player.getId(), game)); - TargetPermanent target = new TargetPermanent(numTargets, numTargets, filter, false); + TargetPermanent target = new TargetPermanent(numTargets, numTargets, filter, true); if (target.canChoose(player.getId(), game)) { - while (!target.isChosen() && player.isInGame()) { - player.chooseTarget(Outcome.Sacrifice, target, source, game); - } + player.chooseTarget(Outcome.Sacrifice, target, source, game); perms.addAll(target.getTargets()); } } From f8f21bd8cefaa7f9f4a88d1ba2947396e77a9ca0 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 10:14:15 +0200 Subject: [PATCH 012/102] * Landfall - Fixed that it also works for Instant and Soceries copied from opponents. Corrected tooltip text formating. --- .../mage/sets/futuresight/RiverOfTears.java | 2 +- .../src/mage/sets/worldwake/Groundswell.java | 58 ++------- .../sets/worldwake/MysteriesOfTheDeep.java | 1 - .../mage/sets/worldwake/RestForTheWeary.java | 1 - .../src/mage/sets/worldwake/SearingBlaze.java | 1 - .../src/mage/sets/worldwake/TombHex.java | 3 +- .../mage/sets/zendikar/SurrakarMarauder.java | 2 + .../abilities/keywords/LandfallTest.java | 115 ++++++++++++++++++ .../abilities/common/LandfallAbility.java | 12 +- .../condition/common/LandfallCondition.java | 8 +- Mage/src/mage/watchers/Watcher.java | 2 +- .../mage/watchers/common/LandfallWatcher.java | 24 +++- 12 files changed, 156 insertions(+), 73 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java diff --git a/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java b/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java index 7ba8ddb7251..881bc577850 100644 --- a/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java +++ b/Mage.Sets/src/mage/sets/futuresight/RiverOfTears.java @@ -50,7 +50,7 @@ public class RiverOfTears extends CardImpl { super(ownerId, 179, "River of Tears", Rarity.RARE, new CardType[]{CardType.LAND}, ""); this.expansionSetCode = "FUT"; - // {tap}: Add {U} to your mana pool. If you played a land this turn, add {B} to your mana pool instead. + // {T}: Add {U} to your mana pool. If you played a land this turn, add {B} to your mana pool instead. this.addAbility(new ConditionalManaAbility(Zone.BATTLEFIELD, new ConditionalManaEffect( new BasicManaEffect(Mana.BlackMana), new BasicManaEffect(Mana.BlueMana), diff --git a/Mage.Sets/src/mage/sets/worldwake/Groundswell.java b/Mage.Sets/src/mage/sets/worldwake/Groundswell.java index 1296415b715..9598babd39e 100644 --- a/Mage.Sets/src/mage/sets/worldwake/Groundswell.java +++ b/Mage.Sets/src/mage/sets/worldwake/Groundswell.java @@ -32,17 +32,13 @@ import java.util.UUID; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Layer; -import mage.constants.Outcome; import mage.constants.Rarity; -import mage.constants.SubLayer; -import mage.abilities.Ability; -import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.condition.LockedInCondition; +import mage.abilities.condition.common.LandfallCondition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.cards.CardImpl; -import mage.game.Game; -import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; -import mage.watchers.Watcher; import mage.watchers.common.LandfallWatcher; /** @@ -55,11 +51,13 @@ public class Groundswell extends CardImpl { super(ownerId, 104, "Groundswell", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G}"); this.expansionSetCode = "WWK"; - - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new GroundswellEffect(Duration.EndOfTurn)); - + // Target creature gets +2/+2 until end of turn. + //Landfall - If you had a land enter the battlefield under your control this turn, that creature gets +4/+4 until end of turn instead. this.getSpellAbility().addWatcher(new LandfallWatcher()); + this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new BoostTargetEffect(4, 4, Duration.EndOfTurn), new BoostTargetEffect(2, 2, Duration.EndOfTurn), + new LockedInCondition(LandfallCondition.getInstance()), + "Target creature gets +2/+2 until end of turn.
Landfall — If you had a land enter the battlefield under your control this turn, that creature gets +4/44 until end of turn instead")); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } public Groundswell(final Groundswell card) { @@ -71,39 +69,3 @@ public class Groundswell extends CardImpl { return new Groundswell(this); } } - -class GroundswellEffect extends ContinuousEffectImpl { - - public GroundswellEffect(Duration duration) { - super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); - staticText = "Target creature gets +2/+2 until end of turn.\nLandfall - If you had a land enter the battlefield under your control this turn, that creature gets +4/+4 until end of turn instead"; - } - - public GroundswellEffect(final GroundswellEffect effect) { - super(effect); - } - - @Override - public GroundswellEffect copy() { - return new GroundswellEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Watcher watcher = game.getState().getWatchers().get("LandPlayed", source.getControllerId()); - Permanent target = (Permanent) game.getPermanent(source.getFirstTarget()); - if (target != null) { - if (watcher != null && watcher.conditionMet()) { - target.addPower(4); - target.addToughness(4); - } - else{ - target.addPower(2); - target.addToughness(2); - } - return true; - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java b/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java index 19a28ff6d7d..804ff917687 100644 --- a/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java +++ b/Mage.Sets/src/mage/sets/worldwake/MysteriesOfTheDeep.java @@ -46,7 +46,6 @@ public class MysteriesOfTheDeep extends CardImpl { super(ownerId, 33, "Mysteries of the Deep", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{4}{U}"); this.expansionSetCode = "WWK"; - // Draw two cards. // Landfall - If you had a land enter the battlefield under your control this turn, draw three cards instead. this.getSpellAbility().addWatcher(new LandfallWatcher()); diff --git a/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java b/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java index 890df85a5a4..08a543c1340 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java +++ b/Mage.Sets/src/mage/sets/worldwake/RestForTheWeary.java @@ -47,7 +47,6 @@ public class RestForTheWeary extends CardImpl { super(ownerId, 18, "Rest for the Weary", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{W}"); this.expansionSetCode = "WWK"; - // Target player gains 4 life. // Landfall - If you had a land enter the battlefield under your control this turn, that player gains 8 life instead. this.getSpellAbility().addWatcher(new LandfallWatcher()); diff --git a/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java b/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java index ec39deffdac..c5b2b73ac36 100644 --- a/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java +++ b/Mage.Sets/src/mage/sets/worldwake/SearingBlaze.java @@ -59,7 +59,6 @@ public class SearingBlaze extends CardImpl { super(ownerId, 90, "Searing Blaze", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}{R}"); this.expansionSetCode = "WWK"; - // Searing Blaze deals 1 damage to target player and 1 damage to target creature that player controls. // Landfall - If you had a land enter the battlefield under your control this turn, Searing Blaze deals 3 damage to that player and 3 damage to that creature instead. this.getSpellAbility().addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/sets/worldwake/TombHex.java b/Mage.Sets/src/mage/sets/worldwake/TombHex.java index 1204445582d..6a42e79ddcf 100644 --- a/Mage.Sets/src/mage/sets/worldwake/TombHex.java +++ b/Mage.Sets/src/mage/sets/worldwake/TombHex.java @@ -50,13 +50,12 @@ public class TombHex extends CardImpl { super(ownerId, 69, "Tomb Hex", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{B}"); this.expansionSetCode = "WWK"; - // Target creature gets -2/-2 until end of turn. // Landfall - If you had a land enter the battlefield under your control this turn, that creature gets -4/-4 until end of turn instead. this.getSpellAbility().addWatcher(new LandfallWatcher()); this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new BoostTargetEffect(-4, -4, Duration.EndOfTurn), new BoostTargetEffect(-2, -2, Duration.EndOfTurn), new LockedInCondition(LandfallCondition.getInstance()), - "Target creature gets -2/-2 until end of turn. Landfall - If you had a land enter the battlefield under your control this turn, that creature gets -4/-4 until end of turn instead")); + "Target creature gets -2/-2 until end of turn.
Landfall — If you had a land enter the battlefield under your control this turn, that creature gets -4/-4 until end of turn instead")); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/sets/zendikar/SurrakarMarauder.java b/Mage.Sets/src/mage/sets/zendikar/SurrakarMarauder.java index 0e317d3a684..1f10bd4ea43 100644 --- a/Mage.Sets/src/mage/sets/zendikar/SurrakarMarauder.java +++ b/Mage.Sets/src/mage/sets/zendikar/SurrakarMarauder.java @@ -52,6 +52,8 @@ public class SurrakarMarauder extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + // Landfall - Whenever a land enters the battlefield under your control, Surrakar Marauder gains intimidate until end of turn. + // (It can't be blocked except by artifact creatures and/or creatures that share a color with it.) this.addAbility(new LandfallAbility(new GainAbilitySourceEffect(IntimidateAbility.getInstance(), Duration.EndOfTurn), false)); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java new file mode 100644 index 00000000000..ef5349b9ae8 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/LandfallTest.java @@ -0,0 +1,115 @@ +/* + * 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.cards.abilities.keywords; + +import mage.abilities.keyword.IntimidateAbility; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class LandfallTest extends CardTestPlayerBase { + + @Test + public void testNormalUse() { + addCard(Zone.BATTLEFIELD, playerA, "Plains",3); + addCard(Zone.HAND, playerA, "Plains"); + + // Instant - {1}{W} + // Target player gains 4 life. + // Landfall - If you had a land enter the battlefield under your control this turn, that player gains 8 life instead. + addCard(Zone.HAND, playerA, "Rest for the Weary",2); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plains"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest for the Weary"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest for the Weary"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Plains", 4); + assertGraveyardCount(playerA, "Rest for the Weary", 2); + assertLife(playerA, 32); // + 8 from 1 turn + 4 from second turn + assertLife(playerB, 20); + + } + /** + * If you Hive Mind an opponent's Rest for the Weary and redirect its target to yourself when it's not your turn, + * the game spits out this message and rolls back to before Rest for the Weary was cast. + * + */ + @Test + public void testHiveMind() { + addCard(Zone.BATTLEFIELD, playerA, "Plains",2); + + // Whenever a player casts an instant or sorcery spell, each other player copies that spell. Each of those players may choose new targets for his or her copy. + addCard(Zone.BATTLEFIELD, playerB, "Hive Mind"); + + // Instant - {1}{W} + // Target player gains 4 life. + // Landfall - If you had a land enter the battlefield under your control this turn, that player gains 8 life instead. + addCard(Zone.HAND, playerA, "Rest for the Weary",1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Rest for the Weary"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Rest for the Weary", 1); + assertLife(playerA, 24); + assertLife(playerB, 24); + + } + + @Test + public void testSurrakarMarauder() { + // Landfall - Whenever a land enters the battlefield under your control, Surrakar Marauder gains intimidate until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Surrakar Marauder",1); + addCard(Zone.HAND, playerA, "Plains"); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plains"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Plains", 1); + + assertAbility(playerA, "Surrakar Marauder", IntimidateAbility.getInstance(), true); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + } + +} diff --git a/Mage/src/mage/abilities/common/LandfallAbility.java b/Mage/src/mage/abilities/common/LandfallAbility.java index 974593e5461..db19efb0ffd 100644 --- a/Mage/src/mage/abilities/common/LandfallAbility.java +++ b/Mage/src/mage/abilities/common/LandfallAbility.java @@ -34,7 +34,6 @@ import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.game.permanent.Permanent; /** @@ -44,10 +43,10 @@ import mage.game.permanent.Permanent; public class LandfallAbility extends TriggeredAbilityImpl { public LandfallAbility(Effect effect, boolean optional) { - super(Zone.BATTLEFIELD, effect, optional); + this(Zone.BATTLEFIELD, effect, optional); } - public LandfallAbility ( Zone zone, Effect effect, Boolean optional ) { + public LandfallAbility (Zone zone, Effect effect, Boolean optional ) { super(zone, effect, optional); } @@ -63,15 +62,12 @@ public class LandfallAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent != null && permanent.getCardType().contains(CardType.LAND) && permanent.getControllerId().equals(this.controllerId)) { - return true; - } - return false; + return permanent != null && permanent.getCardType().contains(CardType.LAND) && permanent.getControllerId().equals(this.controllerId); } @Override public String getRule() { - return "Landfall - Whenever a land enters the battlefield under your control, " + super.getRule(); + return "Landfall — Whenever a land enters the battlefield under your control, " + super.getRule(); } @Override diff --git a/Mage/src/mage/abilities/condition/common/LandfallCondition.java b/Mage/src/mage/abilities/condition/common/LandfallCondition.java index ecfa892b968..490e2b157c5 100644 --- a/Mage/src/mage/abilities/condition/common/LandfallCondition.java +++ b/Mage/src/mage/abilities/condition/common/LandfallCondition.java @@ -3,13 +3,13 @@ package mage.abilities.condition.common; import mage.abilities.Ability; import mage.abilities.condition.Condition; import mage.game.Game; -import mage.watchers.Watcher; +import mage.watchers.common.LandfallWatcher; /** * @author Loki */ public class LandfallCondition implements Condition { - private static LandfallCondition instance = new LandfallCondition(); + private final static LandfallCondition instance = new LandfallCondition(); public static LandfallCondition getInstance() { return instance; @@ -20,7 +20,7 @@ public class LandfallCondition implements Condition { @Override public boolean apply(Game game, Ability source) { - Watcher watcher = game.getState().getWatchers().get("LandPlayed", source.getControllerId()); - return watcher.conditionMet(); + LandfallWatcher watcher = (LandfallWatcher) game.getState().getWatchers().get("LandPlayed"); + return watcher != null && watcher.landPlayed(source.getControllerId()); } } diff --git a/Mage/src/mage/watchers/Watcher.java b/Mage/src/mage/watchers/Watcher.java index 180b7aac839..e9d8520293c 100644 --- a/Mage/src/mage/watchers/Watcher.java +++ b/Mage/src/mage/watchers/Watcher.java @@ -92,7 +92,7 @@ public abstract class Watcher implements Serializable { public boolean conditionMet() { return condition; } - + public void reset() { condition = false; } diff --git a/Mage/src/mage/watchers/common/LandfallWatcher.java b/Mage/src/mage/watchers/common/LandfallWatcher.java index 3a0a659c6e4..f75d4c0d625 100644 --- a/Mage/src/mage/watchers/common/LandfallWatcher.java +++ b/Mage/src/mage/watchers/common/LandfallWatcher.java @@ -1,5 +1,8 @@ package mage.watchers.common; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; import mage.constants.CardType; import mage.constants.WatcherScope; import mage.game.Game; @@ -13,12 +16,15 @@ import mage.watchers.Watcher; */ public class LandfallWatcher extends Watcher { + Set playerPlayedLand = new HashSet<>(); + public LandfallWatcher() { - super("LandPlayed", WatcherScope.PLAYER); + super("LandPlayed", WatcherScope.GAME); } public LandfallWatcher(final LandfallWatcher watcher) { super(watcher); + playerPlayedLand.addAll(playerPlayedLand); } @Override @@ -28,15 +34,21 @@ public class LandfallWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (condition == true) { //no need to check - condition has already occured - return; - } if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { Permanent permanent = game.getPermanent(event.getTargetId()); - if (permanent.getCardType().contains(CardType.LAND) && permanent.getControllerId().equals(this.controllerId)) { - condition = true; + if (permanent.getCardType().contains(CardType.LAND) && !playerPlayedLand.contains(event.getPlayerId())) { + playerPlayedLand.add(event.getPlayerId()); } } } + @Override + public void reset() { + playerPlayedLand.clear(); + super.reset(); + } + + public boolean landPlayed(UUID playerId) { + return playerPlayedLand.contains(playerId); + } } From 907b029bdb85cac3bf0d8dd11b12ecd3efebf985 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 12:43:17 +0200 Subject: [PATCH 013/102] Added target handling for modal spells with multiple targets in different modes. Fixed target handling bugs in test project. --- .../src/mage/sets/theros/GodsWilling.java | 1 - .../abilities/keywords/HexproofTest.java | 2 + .../java/org/mage/test/player/TestPlayer.java | 49 +++++++++++++------ .../base/impl/CardTestPlayerAPIImpl.java | 8 +++ .../GainProtectionFromColorTargetEffect.java | 2 +- 5 files changed, 45 insertions(+), 17 deletions(-) diff --git a/Mage.Sets/src/mage/sets/theros/GodsWilling.java b/Mage.Sets/src/mage/sets/theros/GodsWilling.java index 821ed08b709..f2bfc0b233b 100644 --- a/Mage.Sets/src/mage/sets/theros/GodsWilling.java +++ b/Mage.Sets/src/mage/sets/theros/GodsWilling.java @@ -46,7 +46,6 @@ public class GodsWilling extends CardImpl { super(ownerId, 16, "Gods Willing", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{W}"); this.expansionSetCode = "THS"; - // Target creature you control gains protection from the color of your choice until end of turn. Scry 1. this.getSpellAbility().addEffect(new GainProtectionFromColorTargetEffect(Duration.EndOfTurn)); this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java index 10d0b3c74e2..457f3407508 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java @@ -76,7 +76,9 @@ public class HexproofTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerB, "Island", 4); addCard(Zone.HAND, playerB, "Into the Void"); + // Return up to two target creatures to their owners' hands. castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Into the Void", "Elder of Laurels^Arbor Elf"); + // Target creature you control gets +1/+1 and gains hexproof until end of turn. (It can't be the target of spells or abilities your opponents control.) castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Ranger's Guile", "Elder of Laurels"); setStopAt(2, PhaseStep.END_TURN); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index bf48c2f8738..e538a04b4a5 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -446,7 +446,7 @@ public class TestPlayer extends ComputerPlayer { for (String targetName: targetList) { for (Card card: this.getHand().getCards(((TargetCardInHand)target).getFilter(), game)) { if (card.getName().equals(targetName) || (card.getName()+"-"+card.getExpansionSetCode()).equals(targetName)) { - if (((TargetCardInHand)target).canTarget(source.getControllerId(), card.getId(), source, game) && !target.getTargets().contains(card.getId())) { + if (((TargetCardInHand)target).canTarget(this.getId(), card.getId(), source, game) && !target.getTargets().contains(card.getId())) { target.add(card.getId(), game); targetFound = true; break; @@ -663,42 +663,61 @@ public class TestPlayer extends ComputerPlayer { int index = 0; int targetsSet = 0; for (String targetName: targetList) { + if (targetName.startsWith("mode=")) { + int modeNr = Integer.parseInt(targetName.substring(5, 6)); + if (modeNr == 0 || modeNr > ability.getModes().size()) { + throw new UnsupportedOperationException("Given mode number (" + modeNr + ") not available for " + ability.toString()); + } + int modeCounter = 1; + for (Mode mode :ability.getModes().values()) { + if (modeCounter == modeNr) { + ability.getModes().setMode(mode); + index=0; // reset target index if mode changes + break; + } + modeCounter++; + } + targetName = targetName.substring(6); + } + if (ability.getTargets().size() == 0) { + throw new AssertionError("Ability has no targets. " + ability.toString()); + } + if (index >= ability.getTargets().size()) { + break; // this can happen if targets should be set but can't be used because of hexproof e.g. + } + Target currentTarget = ability.getTargets().get(index); if (targetName.startsWith("targetPlayer=")) { target = targetName.substring(targetName.indexOf("targetPlayer=") + 13); for (Player player: game.getPlayers().values()) { if (player.getName().equals(target)) { - ability.getTargets().get(index).addTarget(player.getId(), ability, game); + currentTarget.addTarget(player.getId(), ability, game); index++; targetsSet++; break; } } } else { - if (ability.getTargets().size() == 0) { - throw new AssertionError("Ability has no targets. " + ability.toString()); - } - for (UUID id: ability.getTargets().get(0).possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) { + for (UUID id: currentTarget.possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) { MageObject object = game.getObject(id); if (object != null && ((!targetName.isEmpty() && object.getName().startsWith(targetName)) || (targetName.isEmpty() && object.getName().isEmpty()))) { - if (index >= ability.getTargets().size()) { - index--; + if (currentTarget.getNumberOfTargets() == 1) { + currentTarget.clearChosen(); } - if (ability.getTargets().get(index).getNumberOfTargets() == 1) { - ability.getTargets().get(index).clearChosen(); - } - if (ability.getTargets().get(index) instanceof TargetCreaturePermanentAmount) { + if (currentTarget instanceof TargetCreaturePermanentAmount) { // supports only to set the complete amount to one target - TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) ability.getTargets().get(index); + TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) currentTarget; targetAmount.setAmount(ability, game); int amount = targetAmount.getAmountRemaining(); targetAmount.addTarget(id, amount,ability, game); targetsSet++; } else { - ability.getTargets().get(index).addTarget(id, ability, game); + currentTarget.addTarget(id, ability, game); targetsSet++; } - index++; + if (currentTarget.getTargets().size() == currentTarget.getMaxNumberOfTargets()) { + index++; + } break; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index 771de0af82d..abb23bd72ad 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -850,6 +850,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement player.addAction(turnNum, step, "activate:Cast " + cardName + "$targetPlayer=" + target.getName() + "$manaInPool=" + manaInPool); } + /** + * + * @param turnNum + * @param step + * @param player + * @param cardName + * @param targetName for modes you can add "mode=3" before target name, multiple targets can be seperated by ^ + */ public void castSpell(int turnNum, PhaseStep step, TestPlayer player, String cardName, String targetName) { player.addAction(turnNum, step, "activate:Cast " + cardName + "$target=" + targetName); } diff --git a/Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorTargetEffect.java index 1767fb43c20..a6af9b8dcc6 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/GainProtectionFromColorTargetEffect.java @@ -51,7 +51,7 @@ public class GainProtectionFromColorTargetEffect extends GainAbilityTargetEffect public GainProtectionFromColorTargetEffect(Duration duration) { super(new ProtectionAbility(new FilterCard()), duration); - choice = new ChoiceColor(); + choice = new ChoiceColor(true); } public GainProtectionFromColorTargetEffect(final GainProtectionFromColorTargetEffect effect) { From bb478fa9d58e595b556cceecbd0d1aeca0f98f6d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 12:44:33 +0200 Subject: [PATCH 014/102] * Heroic - Fixed that heroic only triggered for one mode if a modal spell with heroic targets in differnt modes was used (e.g. Dromoka's Command). --- .../cards/abilities/keywords/HeroicTest.java | 79 +++++++++++++++++++ .../mage/abilities/keyword/HeroicAbility.java | 18 +++-- 2 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HeroicTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HeroicTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HeroicTest.java new file mode 100644 index 00000000000..9ba14bc6326 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HeroicTest.java @@ -0,0 +1,79 @@ +/* + * 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.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class HeroicTest extends CardTestPlayerBase { + + /** + * When casting Dromoka's Command targeting two of my own Heroic creatures, only one of them triggers. + * It appears to be the one targeted with mode 4 (fight) rather than the one targeted with mode 3 (+1/+1 counter). + * Screenshot attached. Reproducible. + */ + + @Test + public void testHeroicWithModal() { + // Heroic - Whenever you cast a spell that targets Favored Hoplite, put a +1/+1 counter on Favored Hoplite and prevent all damage that would be dealt to it this turn. + addCard(Zone.BATTLEFIELD, playerA, "Favored Hoplite", 1); // 1/2 + // Heroic — Whenever you cast a spell that targets Lagonna-Band Trailblazer, put a +1/+1 counter on Lagonna-Band Trailblazer. + addCard(Zone.BATTLEFIELD, playerA, "Lagonna-Band Trailblazer"); // 0/4 + + // Mode 3 = Put a +1/+1 counter on target creature + // Mode 4 = Target creature you control fights target creature you don't control + addCard(Zone.HAND, playerA, "Dromoka's Command", 1); // {G}{W} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dromoka's Command", "mode=3Lagonna-Band Trailblazer^mode=4Favored Hoplite^Silvercoat Lion"); + // Silvercoat lion will be set by AI as only possible target + setModeChoice(playerA, "3"); + setModeChoice(playerA, "4"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Dromoka's Command", 1); + + assertPowerToughness(playerA, "Favored Hoplite", 2, 3); + assertGraveyardCount(playerB, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Lagonna-Band Trailblazer", 2, 6); + + } + +} diff --git a/Mage/src/mage/abilities/keyword/HeroicAbility.java b/Mage/src/mage/abilities/keyword/HeroicAbility.java index e7501685363..5b89f5d65b1 100644 --- a/Mage/src/mage/abilities/keyword/HeroicAbility.java +++ b/Mage/src/mage/abilities/keyword/HeroicAbility.java @@ -83,17 +83,19 @@ public class HeroicAbility extends TriggeredAbilityImpl { private boolean checkSpell(Spell spell, Game game) { if (spell != null) { SpellAbility sa = spell.getSpellAbility(); - for (Target target : sa.getTargets()) { - if (!target.isNotTarget() && target.getTargets().contains(this.getSourceId())) { - return true; - } - } - for (Effect effect : sa.getEffects()) { - for (UUID targetId : effect.getTargetPointer().getTargets(game, sa)) { - if (targetId.equals(this.getSourceId())) { + for(UUID modeId :sa.getModes().getSelectedModes()) { + for (Target target : sa.getModes().get(modeId).getTargets()) { + if (!target.isNotTarget() && target.getTargets().contains(this.getSourceId())) { return true; } } + for (Effect effect : sa.getModes().get(modeId).getEffects()) { + for (UUID targetId : effect.getTargetPointer().getTargets(game, sa)) { + if (targetId.equals(this.getSourceId())) { + return true; + } + } + } } } return false; From 54ceaae22209adb9cbc42b5cbfeb11fda1729ce4 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 13:30:09 +0200 Subject: [PATCH 015/102] * Tajuru Preserver - Fixed that also opponent was not able to sacrifice permants due to the effect. --- .../riseoftheeldrazi/TajuruPreserver.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/TajuruPreserver.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/TajuruPreserver.java index f242ffd914d..8baf7705514 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/TajuruPreserver.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/TajuruPreserver.java @@ -40,6 +40,7 @@ import mage.game.permanent.PermanentCard; import mage.game.stack.Spell; import java.util.UUID; +import mage.game.permanent.Permanent; /** * @author noxx @@ -97,15 +98,18 @@ class TajuruPreserverEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { - MageObject object = game.getObject(event.getSourceId()); - if (object instanceof PermanentCard) { - if (game.getOpponents(source.getControllerId()).contains(((PermanentCard)object).getControllerId())) { - return true; + Permanent permanent = game.getPermanent(event.getTargetId()); + if (permanent != null && permanent.getControllerId().equals(source.getControllerId())) { + MageObject object = game.getObject(event.getSourceId()); + if (object instanceof PermanentCard) { + if (game.getOpponents(source.getControllerId()).contains(((PermanentCard)object).getControllerId())) { + return true; + } } - } - if (object instanceof Spell) { - if (game.getOpponents(source.getControllerId()).contains(((Spell)object).getControllerId())) { - return true; + if (object instanceof Spell) { + if (game.getOpponents(source.getControllerId()).contains(((Spell)object).getControllerId())) { + return true; + } } } return false; From a541d24a20b1c2f6e825b8e8a4aa318ebafd6fc5 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 15:14:44 +0200 Subject: [PATCH 016/102] * Treacherous Pit-Dweller - Fixed that the enteres the battlefield ability was not implemented correctly. --- .../avacynrestored/TreacherousPitDweller.java | 28 +++++++++++++++---- .../src/mage/sets/newphyrexia/TorporOrb.java | 14 ++++++---- .../test/cards/replacement/TorporOrbTest.java | 28 ++++++++++++++++++- .../EntersBattlefieldTriggeredAbility.java | 2 +- 4 files changed, 60 insertions(+), 12 deletions(-) diff --git a/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java b/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java index 8e5033a0b2d..65723eb5af0 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java @@ -30,7 +30,6 @@ package mage.sets.avacynrestored; import mage.constants.*; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.UndyingAbility; import mage.cards.CardImpl; @@ -39,6 +38,9 @@ import mage.game.permanent.Permanent; import mage.target.common.TargetOpponent; import java.util.UUID; +import mage.abilities.TriggeredAbilityImpl; +import mage.game.events.EntersTheBattlefieldEvent; +import mage.game.events.GameEvent; /** * @author noxx @@ -70,30 +72,46 @@ public class TreacherousPitDweller extends CardImpl { } } -class TreacherousPitDwellerTriggeredAbility extends ZoneChangeTriggeredAbility { +class TreacherousPitDwellerTriggeredAbility extends TriggeredAbilityImpl { private static final String ruleText = "When {this} enters the battlefield from a graveyard, "; public TreacherousPitDwellerTriggeredAbility() { - super(Zone.GRAVEYARD, Zone.BATTLEFIELD, new TreacherousPitDwellerEffect(), ruleText, false); + super(Zone.BATTLEFIELD, new TreacherousPitDwellerEffect(),false); addTarget(new TargetOpponent()); } public TreacherousPitDwellerTriggeredAbility(final TreacherousPitDwellerTriggeredAbility ability) { super(ability); } - + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getTargetId().equals(getSourceId()) && ((EntersTheBattlefieldEvent) event).getFromZone().equals(Zone.GRAVEYARD); + } + @Override public TreacherousPitDwellerTriggeredAbility copy() { return new TreacherousPitDwellerTriggeredAbility(this); } + + @Override + public String getRule() { + return ruleText + super.getRule(); + } + } class TreacherousPitDwellerEffect extends ContinuousEffectImpl { public TreacherousPitDwellerEffect() { super(Duration.Custom, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); - staticText = "Target opponent gains control of {this}"; + staticText = "target opponent gains control of {this}"; } public TreacherousPitDwellerEffect(final TreacherousPitDwellerEffect effect) { diff --git a/Mage.Sets/src/mage/sets/newphyrexia/TorporOrb.java b/Mage.Sets/src/mage/sets/newphyrexia/TorporOrb.java index 22e0e3103fa..59519623423 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/TorporOrb.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/TorporOrb.java @@ -28,8 +28,8 @@ package mage.sets.newphyrexia; import java.util.UUID; +import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.TriggeredAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.cards.CardImpl; @@ -94,12 +94,16 @@ class TorporOrbEffect extends ContinuousRuleModifyingEffectImpl { } return false; } - @Override - public boolean apply(Game game, Ability source) { - return true; + public String getInfoMessage(Ability source, GameEvent event, Game game) { + MageObject mageObject = game.getObject(event.getSourceId()); + MageObject sourceObject = game.getObject(source.getSourceId()); + if (mageObject != null && sourceObject != null) { + return sourceObject.getLogName() + " prevented ability of " + mageObject.getLogName() + " to trigger"; + } + return null; } - + @Override public TorporOrbEffect copy() { return new TorporOrbEffect(this); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java index 49ccc89860c..441d68754b9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/TorporOrbTest.java @@ -14,7 +14,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; public class TorporOrbTest extends CardTestPlayerBase { @Test - public void testCard() { + public void testWallOfOmens() { addCard(Zone.BATTLEFIELD, playerA, "Torpor Orb"); addCard(Zone.HAND, playerA, "Wall of Omens"); addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); @@ -32,4 +32,30 @@ public class TorporOrbTest extends CardTestPlayerBase { assertHandCount(playerA, 0); } + /** + * Treacherous Pit-Dweller doesnt function properly with Torpor Orb and Hushwing Gryff + */ + @Test + public void testPitTweller() { + addCard(Zone.BATTLEFIELD, playerB, "Torpor Orb"); + addCard(Zone.BATTLEFIELD, playerB, "Treacherous Pit-Dweller"); // 4/3 + + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + attack(2, playerB, "Treacherous Pit-Dweller"); + castSpell(2, PhaseStep.DECLARE_ATTACKERS, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); + + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + + assertPermanentCount(playerB, "Treacherous Pit-Dweller", 1); + assertPowerToughness(playerB, "Treacherous Pit-Dweller", 5,4); + } + } diff --git a/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java b/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java index f34efeadc23..ddaf6be2b8a 100644 --- a/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/EntersBattlefieldTriggeredAbility.java @@ -81,7 +81,7 @@ public class EntersBattlefieldTriggeredAbility extends TriggeredAbilityImpl { if (noRule) { return super.getRule(); } - return new StringBuilder(rulePrefix != null ? rulePrefix : "").append("When {this} enters the battlefield, ").append(super.getRule()).toString(); + return (rulePrefix != null ? rulePrefix : "") + "When {this} enters the battlefield, "+ super.getRule(); } @Override From 726fc48e49896bc7ae9ba1b333e7048200fef7b2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 15:15:45 +0200 Subject: [PATCH 017/102] * Hushwing Gryff - Fixed not correct implemented rule modifying effect. --- .../mage/sets/magic2015/HushwingGryff.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2015/HushwingGryff.java b/Mage.Sets/src/mage/sets/magic2015/HushwingGryff.java index 1fe61e0b698..3cbc4aba1c6 100644 --- a/Mage.Sets/src/mage/sets/magic2015/HushwingGryff.java +++ b/Mage.Sets/src/mage/sets/magic2015/HushwingGryff.java @@ -36,13 +36,13 @@ import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; +import mage.constants.AbilityType; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -104,18 +104,18 @@ class HushwingGryffEffect extends ContinuousRuleModifyingEffectImpl { return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; } - @Override + @Override public boolean applies(GameEvent event, Ability source, Game game) { - Permanent permanent = ((EntersTheBattlefieldEvent)event).getTarget(); - if (permanent != null && permanent.getCardType().contains(CardType.CREATURE)) { - // Because replacement events have to be executed - // call replaceEvent here without calling the triggering event after - game.getContinuousEffects().replaceEvent(event, game); - return true; + Ability ability = (Ability) getValue("targetAbility"); + if (ability != null && AbilityType.TRIGGERED.equals(ability.getAbilityType())) { + Permanent p = game.getPermanent(event.getTargetId()); + if (p != null && p.getCardType().contains(CardType.CREATURE)) { + return true; + } } return false; } - + @Override public boolean apply(Game game, Ability source) { return true; @@ -126,4 +126,4 @@ class HushwingGryffEffect extends ContinuousRuleModifyingEffectImpl { return new HushwingGryffEffect(this); } -} \ No newline at end of file +} From 02aaf150db04d41dcde617f36cafea98388fc218 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 15:16:12 +0200 Subject: [PATCH 018/102] Added some tests. --- .../NarsetEnlightenedMaster.java | 31 ++++---- .../src/mage/sets/theros/OrdealOfHeliod.java | 1 - .../asthough/PlayFromNonHandZoneTest.java | 45 +++++++++++ .../single/ths/OrdealEnchantmentsTest.java | 74 +++++++++++++++++++ 4 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/ths/OrdealEnchantmentsTest.java diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/NarsetEnlightenedMaster.java b/Mage.Sets/src/mage/sets/khansoftarkir/NarsetEnlightenedMaster.java index f67946340fd..cd3ee2eee14 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/NarsetEnlightenedMaster.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/NarsetEnlightenedMaster.java @@ -27,6 +27,7 @@ */ package mage.sets.khansoftarkir; +import java.util.List; import java.util.UUID; import mage.MageInt; import mage.MageObject; @@ -101,18 +102,16 @@ class NarsetEnlightenedMasterExileEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); MageObject sourceObject = game.getObject(source.getSourceId()); if (player != null && sourceObject != null) { - for (int i = 0; i < 4; i++) { - if (player.getLibrary().size() > 0) { - Card card = player.getLibrary().getFromTop(game); - if (card != null) { - player.moveCardToExileWithInfo(card, CardUtil.getCardExileZoneId(game, source), sourceObject.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true); - if (!card.getCardType().contains(CardType.CREATURE) && !card.getCardType().contains(CardType.LAND)) { - ContinuousEffect effect = new NarsetEnlightenedMasterCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); - game.addEffect(effect, source); - } - } - } + List cards = player.getLibrary().getTopCards(game, 4); + player.moveCards(cards, Zone.LIBRARY, Zone.EXILED, source, game); + for (Card card : cards) { + if (game.getState().getZone(card.getId()) == Zone.EXILED && + !card.getCardType().contains(CardType.CREATURE) && + !card.getCardType().contains(CardType.LAND)) { + ContinuousEffect effect = new NarsetEnlightenedMasterCastFromExileEffect(); + effect.setTargetPointer(new FixedTarget(card.getId())); + game.addEffect(effect, source); + } } return true; } @@ -150,10 +149,12 @@ class NarsetEnlightenedMasterCastFromExileEffect extends AsThoughEffectImpl { public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { if (objectId.equals(getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) { Card card = game.getCard(objectId); - if (card != null && game.getState().getZone(objectId) == Zone.EXILED) { + if (card != null) { Player player = game.getPlayer(affectedControllerId); - player.setCastSourceIdWithAlternateMana(objectId, null); - return true; + if (player != null) { + player.setCastSourceIdWithAlternateMana(objectId, null); + return true; + } } } return false; diff --git a/Mage.Sets/src/mage/sets/theros/OrdealOfHeliod.java b/Mage.Sets/src/mage/sets/theros/OrdealOfHeliod.java index 19bda410efa..cb280e33305 100644 --- a/Mage.Sets/src/mage/sets/theros/OrdealOfHeliod.java +++ b/Mage.Sets/src/mage/sets/theros/OrdealOfHeliod.java @@ -58,7 +58,6 @@ public class OrdealOfHeliod extends CardImpl { this.expansionSetCode = "THS"; this.subtype.add("Aura"); - // Enchant creature TargetPermanent auraTarget = new TargetCreaturePermanent(); this.getSpellAbility().addTarget(auraTarget); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java index a55b021a775..0c2a6912486 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/asthough/PlayFromNonHandZoneTest.java @@ -79,4 +79,49 @@ public class PlayFromNonHandZoneTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Worldheart Phoenix", 1); } + + + @Test + public void testNarsetEnlightenedMaster() { + // First strike + // Hexproof + // Whenever Narset, Enlightented Master attacks, exile the top four cards of your library. Until end of turn, you may cast noncreature cards exiled with Narset this turn without paying their mana costs. + addCard(Zone.BATTLEFIELD, playerB, "Narset, Enlightened Master", 1); + + skipInitShuffling(); + addCard(Zone.LIBRARY, playerB, "Silvercoat Lion"); + addCard(Zone.LIBRARY, playerB, "Abzan Banner"); + // Ferocious - If you control a creature with power 4 or greater, you may cast Dragon Grip as though it had flash. (You may cast it any time you could cast an instant.) + // Enchant creature + // Enchanted creature gets +2/+0 and has first strike. + addCard(Zone.LIBRARY, playerB, "Dragon Grip"); + // You gain 2 life for each creature you control. + addCard(Zone.LIBRARY, playerB, "Peach Garden Oath"); + addCard(Zone.LIBRARY, playerB, "Plains"); + + attack(2, playerB, "Narset, Enlightened Master"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Silvercoat Lion"); // can't be cast from exile + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Abzan Banner"); // can be cast from exile + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Dragon Grip", "Narset, Enlightened Master"); // can be cast from exile + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Peach Garden Oath"); // can be cast from exile + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertExileCount("Silvercoat Lion", 1); + assertPermanentCount(playerB, "Abzan Banner", 1); + assertPermanentCount(playerB, "Dragon Grip", 1); + assertGraveyardCount(playerB, "Peach Garden Oath", 1); + + assertPowerToughness(playerB, "Narset, Enlightened Master", 5, 2); + + assertHandCount(playerB, "Plains", 1); + assertLife(playerA, 17); + assertLife(playerB, 22); + + + } + + } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/OrdealEnchantmentsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/OrdealEnchantmentsTest.java new file mode 100644 index 00000000000..fb492135112 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/OrdealEnchantmentsTest.java @@ -0,0 +1,74 @@ +/* + * 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.cards.single.ths; + +import mage.constants.CardType; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class OrdealEnchantmentsTest extends CardTestPlayerBase { + + @Test + public void testOrdealofHeliod() { + addCard(Zone.BATTLEFIELD, playerB, "Plains", 2); + // Enchant creature + // Whenever enchanted creature attacks, put a +1/+1 counter on it. Then if it has three or more +1/+1 counters on it, sacrifice Ordeal of Heliod. + // When you sacrifice Ordeal of Heliod, you gain 10 life. + addCard(Zone.HAND, playerB, "Ordeal of Heliod"); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Ordeal of Heliod", "Silvercoat Lion"); + + attack(2, playerB, "Silvercoat Lion"); + attack(4, playerB, "Silvercoat Lion"); + attack(6, playerB, "Silvercoat Lion"); + + setStopAt(6, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerA, 8); // 3 + 4 + 5 = 12 + assertLife(playerB, 30); + + assertGraveyardCount(playerB, "Ordeal of Heliod", 1); + + assertPermanentCount(playerB, "Silvercoat Lion", 1); + assertPowerToughness(playerB, "Silvercoat Lion", 5,5); + } + + +} \ No newline at end of file From af1892a6e7052f980c0ae81d6f403f23e9d61bd3 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 30 May 2015 17:13:58 +0200 Subject: [PATCH 019/102] * Fallen Ferromancer - Added missing Infect ability. --- Mage.Sets/src/mage/sets/newphyrexia/FallenFerromancer.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Mage.Sets/src/mage/sets/newphyrexia/FallenFerromancer.java b/Mage.Sets/src/mage/sets/newphyrexia/FallenFerromancer.java index 3214d236871..1874096dd70 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/FallenFerromancer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/FallenFerromancer.java @@ -36,6 +36,7 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.keyword.InfectAbility; import mage.cards.CardImpl; import mage.target.common.TargetCreatureOrPlayer; @@ -53,6 +54,11 @@ public class FallenFerromancer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); + + // Infect (This creature deals damage to creatures in the form of -1/-1 counters and to players in the form of poison counters.) + this.addAbility(InfectAbility.getInstance()); + + // {1}{R}, {T}: Fallen Ferromancer deals 1 damage to target creature or player. SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new TapSourceCost()); ability.addCost(new ManaCostsImpl("{1}{R}")); ability.addTarget(new TargetCreatureOrPlayer()); From 1b690e5c8c4c66a20314ad53f5d0d5ba3e16bd90 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 31 May 2015 18:44:02 +0200 Subject: [PATCH 020/102] * Fixed target change handling (e.g. with opponent filter - fixes #574). Added some tests. --- .../java/mage/player/ai/ComputerPlayer.java | 92 ++++--- .../src/mage/player/human/HumanPlayer.java | 20 +- .../avacynrestored/TreacherousPitDweller.java | 6 +- .../sets/fatereforged/MonasteryMentor.java | 1 + .../fatereforged/WhisperwoodElemental.java | 2 +- .../sets/mercadianmasques/Misdirection.java | 2 +- .../src/mage/sets/visions/CityOfSolitude.java | 6 - .../abilities/keywords/ManifestTest.java | 36 +++ .../TargetOpponentGainsControlTest.java | 20 +- .../test/cards/single/MisdirectionTest.java | 70 ++++- .../cards/triggers/SpellCastTriggerTest.java | 30 +- .../java/org/mage/test/player/TestPlayer.java | 8 +- Mage/src/mage/abilities/Ability.java | 2 + Mage/src/mage/abilities/AbilityImpl.java | 1 + .../common/ChooseNewTargetsTargetEffect.java | 1 + Mage/src/mage/game/stack/Spell.java | 215 +-------------- Mage/src/mage/game/stack/StackAbility.java | 199 +------------- Mage/src/mage/game/stack/StackObjImpl.java | 260 ++++++++++++++++++ Mage/src/mage/players/PlayerImpl.java | 3 +- Mage/src/mage/target/Target.java | 4 + Mage/src/mage/target/TargetAmount.java | 3 +- Mage/src/mage/target/TargetImpl.java | 34 ++- 22 files changed, 531 insertions(+), 484 deletions(-) create mode 100644 Mage/src/mage/game/stack/StackObjImpl.java diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index f0bce292705..df0f14580ba 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -204,11 +204,16 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (log.isDebugEnabled()) { log.debug("chooseTarget: " + outcome.toString() + ":" + target.toString()); } - UUID opponentId = game.getOpponents(playerId).iterator().next(); + // sometimes a target aelection can be made from a player that does not control the ability + UUID abilityControllerId = playerId; + if (target.getTargetController() != null && target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } + UUID opponentId = game.getOpponents(abilityControllerId).iterator().next(); if (target instanceof TargetPlayer) { if (outcome.isGood()) { - if (target.canTarget(playerId, game)) { - target.add(playerId, game); + if (target.canTarget(abilityControllerId, game)) { + target.add(abilityControllerId, game); return true; } if (target.isRequired(sourceId, game)) { @@ -223,8 +228,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { return true; } if (target.isRequired(sourceId, game)) { - if (target.canTarget(playerId, game)) { - target.add(playerId, game); + if (target.canTarget(abilityControllerId, game)) { + target.add(abilityControllerId, game); return true; } } @@ -257,12 +262,12 @@ public class ComputerPlayer extends PlayerImpl implements Player { } if (target instanceof TargetControlledPermanent) { List targets; - targets = threats(playerId, sourceId, ((TargetControlledPermanent) target).getFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, sourceId, ((TargetControlledPermanent) target).getFilter(), game, target.getTargets()); if (!outcome.isGood()) { Collections.reverse(targets); } for (Permanent permanent : targets) { - if (((TargetControlledPermanent) target).canTarget(playerId, permanent.getId(), sourceId, game, false) && !target.getTargets().contains(permanent.getId())) { + if (((TargetControlledPermanent) target).canTarget(abilityControllerId, permanent.getId(), sourceId, game, false) && !target.getTargets().contains(permanent.getId())) { target.add(permanent.getId(), game); return true; } @@ -275,13 +280,13 @@ public class ComputerPlayer extends PlayerImpl implements Player { targets = threats(null, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); } else { if (outcome.isGood()) { - targets = threats(playerId, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); } else { targets = threats(opponentId, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); } } for (Permanent permanent : targets) { - if (((TargetPermanent) target).canTarget(playerId, permanent.getId(), null, game) && !target.getTargets().contains(permanent.getId())) { + if (((TargetPermanent) target).canTarget(abilityControllerId, permanent.getId(), null, game) && !target.getTargets().contains(permanent.getId())) { target.add(permanent.getId(), game); return true; } @@ -309,13 +314,13 @@ public class ComputerPlayer extends PlayerImpl implements Player { List targets; TargetCreatureOrPlayer t = ((TargetCreatureOrPlayer) target); if (outcome.isGood()) { - targets = threats(playerId, sourceId, ((FilterCreatureOrPlayer) t.getFilter()).getCreatureFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, sourceId, ((FilterCreatureOrPlayer) t.getFilter()).getCreatureFilter(), game, target.getTargets()); } else { targets = threats(opponentId, sourceId, ((FilterCreatureOrPlayer) t.getFilter()).getCreatureFilter(), game, target.getTargets()); } for (Permanent permanent : targets) { List alreadyTargetted = target.getTargets(); - if (t.canTarget(playerId, permanent.getId(), null, game)) { + if (t.canTarget(abilityControllerId, permanent.getId(), null, game)) { if (alreadyTargetted != null && !alreadyTargetted.contains(permanent.getId())) { target.add(permanent.getId(), game); return true; @@ -323,8 +328,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } if (outcome.isGood()) { - if (target.canTarget(playerId, null, game)) { - target.add(playerId, game); + if (target.canTarget(abilityControllerId, null, game)) { + target.add(abilityControllerId, game); return true; } } else { @@ -341,7 +346,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (target instanceof TargetPermanentOrPlayer) { List targets; TargetPermanentOrPlayer t = ((TargetPermanentOrPlayer) target); - List ownedTargets = threats(playerId, sourceId, ((FilterPermanentOrPlayer) t.getFilter()).getPermanentFilter(), game, target.getTargets());; + List ownedTargets = threats(abilityControllerId, sourceId, ((FilterPermanentOrPlayer) t.getFilter()).getPermanentFilter(), game, target.getTargets());; List opponentTargets = threats(opponentId, sourceId, ((FilterPermanentOrPlayer) t.getFilter()).getPermanentFilter(), game, target.getTargets()); if (outcome.isGood()) { targets = ownedTargets; @@ -358,8 +363,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } if (outcome.isGood()) { - if (target.canTarget(playerId, null, game)) { - target.add(playerId, game); + if (target.canTarget(abilityControllerId, null, game)) { + target.add(abilityControllerId, game); return true; } } else { @@ -375,8 +380,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { target.add(opponentId, game); return true; } - if (target.canTarget(playerId, null, game)) { - target.add(playerId, game); + if (target.canTarget(abilityControllerId, null, game)) { + target.add(abilityControllerId, game); return true; } if (outcome.isGood()) { // no other valid targets so use a permanent @@ -415,7 +420,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (target instanceof TargetCardInYourGraveyard) { List alreadyTargetted = target.getTargets(); - List cards = new ArrayList<>(game.getPlayer(playerId).getGraveyard().getCards(game)); + List cards = new ArrayList<>(game.getPlayer(abilityControllerId).getGraveyard().getCards(game)); while(!cards.isEmpty()) { Card card = pickTarget(cards, outcome, target, null, game); if (card != null && alreadyTargetted != null && !alreadyTargetted.contains(card.getId())) { @@ -428,7 +433,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (target instanceof TargetSource) { Set targets; TargetSource t = ((TargetSource) target); - targets = t.possibleTargets(sourceId, playerId, game); + targets = t.possibleTargets(sourceId, abilityControllerId, game); for (UUID targetId : targets) { MageObject targetObject = game.getObject(targetId); if (targetObject != null) { @@ -455,28 +460,33 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (log.isDebugEnabled()) { log.debug("chooseTarget: " + outcome.toString() + ":" + target.toString()); } - UUID opponentId = game.getOpponents(playerId).iterator().next(); + // sometimes a target selection can be made from a player that does not control the ability + UUID abilityControllerId = playerId; + if (target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } + UUID opponentId = game.getOpponents(abilityControllerId).iterator().next(); if (target instanceof TargetPlayer) { if (outcome.isGood()) { - if (target.canTarget(playerId, playerId, source, game)) { + if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { target.addTarget(playerId, source, game); return true; } if (target.isRequired(source)) { - if (target.canTarget(playerId, opponentId, source, game)) { + if (target.canTarget(abilityControllerId, opponentId, source, game)) { target.addTarget(opponentId, source, game); return true; } } } else { - if (target.canTarget(playerId, opponentId, source, game)) { + if (target.canTarget(abilityControllerId, opponentId, source, game)) { target.addTarget(opponentId, source, game); return true; } if (target.isRequired(source)) { - if (target.canTarget(playerId, playerId, source, game)) { - target.addTarget(playerId, source, game); + if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { + target.addTarget(abilityControllerId, source, game); return true; } } @@ -526,12 +536,12 @@ public class ComputerPlayer extends PlayerImpl implements Player { } if (target instanceof TargetControlledPermanent) { List targets; - targets = threats(playerId, source.getSourceId(), ((TargetControlledPermanent)target).getFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, source.getSourceId(), ((TargetControlledPermanent)target).getFilter(), game, target.getTargets()); if (!outcome.isGood()) { Collections.reverse(targets); } for (Permanent permanent: targets) { - if (((TargetControlledPermanent)target).canTarget(playerId, permanent.getId(), source, game)) { + if (((TargetControlledPermanent)target).canTarget(abilityControllerId, permanent.getId(), source, game)) { target.addTarget(permanent.getId(), source, game); if (target.getNumberOfTargets() <= target.getTargets().size() && (!outcome.isGood() || target.getMaxNumberOfTargets() <= target.getTargets().size())) { return true; @@ -545,7 +555,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { List targets; boolean outcomeTargets = true; if (outcome.isGood()) { - targets = threats(playerId, source == null?null:source.getSourceId(), ((TargetPermanent)target).getFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, source == null?null:source.getSourceId(), ((TargetPermanent)target).getFilter(), game, target.getTargets()); } else { targets = threats(opponentId, source == null?null:source.getSourceId(), ((TargetPermanent)target).getFilter(), game, target.getTargets()); @@ -557,7 +567,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { //targets = game.getBattlefield().getActivePermanents(((TargetPermanent)target).getFilter(), playerId, game); } for (Permanent permanent: targets) { - if (((TargetPermanent)target).canTarget(playerId, permanent.getId(), source, game)) { + if (((TargetPermanent)target).canTarget(abilityControllerId, permanent.getId(), source, game)) { target.addTarget(permanent.getId(), source, game); if (!outcomeTargets || target.getMaxNumberOfTargets() <= target.getTargets().size()) { return true; @@ -570,7 +580,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { List targets; TargetCreatureOrPlayer t = ((TargetCreatureOrPlayer)target); if (outcome.isGood()) { - targets = threats(playerId, source.getSourceId(), ((FilterCreatureOrPlayer)t.getFilter()).getCreatureFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, source.getSourceId(), ((FilterCreatureOrPlayer)t.getFilter()).getCreatureFilter(), game, target.getTargets()); } else { targets = threats(opponentId, source.getSourceId(), ((FilterCreatureOrPlayer)t.getFilter()).getCreatureFilter(), game, target.getTargets()); @@ -578,8 +588,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (targets.isEmpty()) { if (outcome.isGood()) { - if (target.canTarget(playerId, source, game)) { - target.addTarget(playerId, source, game); + if (target.canTarget(abilityControllerId, source, game)) { + target.addTarget(abilityControllerId, source, game); return true; } } @@ -596,7 +606,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } for (Permanent permanent : targets) { List alreadyTargetted = target.getTargets(); - if (t.canTarget(playerId, permanent.getId(), source, game)) { + if (t.canTarget(abilityControllerId, permanent.getId(), source, game)) { if (alreadyTargetted != null && !alreadyTargetted.contains(permanent.getId())) { target.addTarget(permanent.getId(), source, game); return true; @@ -605,8 +615,8 @@ public class ComputerPlayer extends PlayerImpl implements Player { } if (outcome.isGood()) { - if (target.canTarget(playerId, source, game)) { - target.addTarget(playerId, source, game); + if (target.canTarget(abilityControllerId, source, game)) { + target.addTarget(abilityControllerId, source, game); return true; } } @@ -634,7 +644,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { return false; } if (target instanceof TargetCardInLibrary) { - List cards = new ArrayList<>(game.getPlayer(playerId).getLibrary().getCards(game)); + List cards = new ArrayList<>(game.getPlayer(abilityControllerId).getLibrary().getCards(game)); Card card = pickTarget(cards, outcome, target, source, game); if (card != null) { target.addTarget(card.getId(), source, game); @@ -643,7 +653,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { return false; } if (target instanceof TargetCardInYourGraveyard) { - List cards = new ArrayList<>(game.getPlayer(playerId).getGraveyard().getCards(game)); + List cards = new ArrayList<>(game.getPlayer(abilityControllerId).getGraveyard().getCards(game)); while(!target.isChosen() && !cards.isEmpty()) { Card card = pickTarget(cards, outcome, target, source, game); if (card != null) { @@ -680,7 +690,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { List targets; boolean outcomeTargets = true; if (outcome.isGood()) { - targets = threats(playerId, source == null?null:source.getSourceId(), ((TargetSpellOrPermanent)target).getPermanentFilter(), game, target.getTargets()); + targets = threats(abilityControllerId, source == null?null:source.getSourceId(), ((TargetSpellOrPermanent)target).getPermanentFilter(), game, target.getTargets()); } else { targets = threats(opponentId, source == null?null:source.getSourceId(), ((TargetSpellOrPermanent)target).getPermanentFilter(), game, target.getTargets()); @@ -692,7 +702,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { //targets = game.getBattlefield().getActivePermanents(((TargetPermanent)target).getFilter(), playerId, game); } for (Permanent permanent: targets) { - if (((TargetPermanent)target).canTarget(playerId, permanent.getId(), source, game)) { + if (((TargetPermanent)target).canTarget(abilityControllerId, permanent.getId(), source, game)) { target.addTarget(permanent.getId(), source, game); if (!outcomeTargets || target.getMaxNumberOfTargets() <= target.getTargets().size()) { return true; @@ -715,7 +725,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } if (target instanceof TargetCardInOpponentsGraveyard) { List cards = new ArrayList<>(); - for (UUID uuid: game.getOpponents(playerId)) { + for (UUID uuid: game.getOpponents(abilityControllerId)) { Player player = game.getPlayer(uuid); if (player != null) { cards.addAll(player.getGraveyard().getCards(game)); diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 5a3750c595a..9faf9554cea 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -235,11 +235,15 @@ public class HumanPlayer extends PlayerImpl { @Override public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game, Map options) { updateGameStatePriority("choose(5)", game); + UUID abilityControllerId = playerId; + if (target.getTargetController() != null && target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } if (options == null) { options = new HashMap<>(); } while (!abort) { - Set targetIds = target.possibleTargets(sourceId, playerId, game); + Set targetIds = target.possibleTargets(sourceId, abilityControllerId, game); if (targetIds == null || targetIds.isEmpty()) { return false; } @@ -251,14 +255,14 @@ public class HumanPlayer extends PlayerImpl { List chosen = target.getTargets(); options.put("chosen", (Serializable)chosen); - game.fireSelectTargetEvent(playerId, target.getMessage(), targetIds, required, getOptions(target, options)); + game.fireSelectTargetEvent(getId(), target.getMessage(), targetIds, required, getOptions(target, options)); waitForResponse(game); if (response.getUUID() != null) { if (!targetIds.contains(response.getUUID())) { continue; } if (target instanceof TargetPermanent) { - if (((TargetPermanent)target).canTarget(playerId, response.getUUID(), sourceId, game, false)) { + if (((TargetPermanent)target).canTarget(abilityControllerId, response.getUUID(), sourceId, game, false)) { target.add(response.getUUID(), game); if(target.doneChosing()){ return true; @@ -306,13 +310,17 @@ public class HumanPlayer extends PlayerImpl { @Override public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) { updateGameStatePriority("chooseTarget", game); + UUID abilityControllerId = playerId; + if (target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } while (!abort) { - Set possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), playerId, game); + Set possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), abilityControllerId, game); boolean required = target.isRequired(source); if (possibleTargets.isEmpty() || target.getTargets().size() >= target.getNumberOfTargets()) { required = false; } - game.fireSelectTargetEvent(playerId, target.getMessage(), possibleTargets, required, getOptions(target, null)); + game.fireSelectTargetEvent(getId(), target.getMessage(), possibleTargets, required, getOptions(target, null)); waitForResponse(game); if (response.getUUID() != null) { if (target.getTargets().contains(response.getUUID())) { @@ -320,7 +328,7 @@ public class HumanPlayer extends PlayerImpl { continue; } if (possibleTargets.contains(response.getUUID())) { - if (target.canTarget(playerId, response.getUUID(), source, game)) { + if (target.canTarget(abilityControllerId, response.getUUID(), source, game)) { target.addTarget(response.getUUID(), source, game); if(target.doneChosing()){ return true; diff --git a/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java b/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java index 65723eb5af0..ac03b230859 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/TreacherousPitDweller.java @@ -41,6 +41,7 @@ import java.util.UUID; import mage.abilities.TriggeredAbilityImpl; import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; +import mage.players.Player; /** * @author noxx @@ -126,8 +127,9 @@ class TreacherousPitDwellerEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { Permanent permanent = (Permanent) source.getSourceObjectIfItStillExists(game); - if (permanent != null) { - return permanent.changeControllerId(source.getFirstTarget(), game); + Player targetOpponent = game.getPlayer(source.getFirstTarget()); + if (permanent != null && targetOpponent != null) { + return permanent.changeControllerId(targetOpponent.getId(), game); } else { discard(); } diff --git a/Mage.Sets/src/mage/sets/fatereforged/MonasteryMentor.java b/Mage.Sets/src/mage/sets/fatereforged/MonasteryMentor.java index e30f0ac8769..73871d05660 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/MonasteryMentor.java +++ b/Mage.Sets/src/mage/sets/fatereforged/MonasteryMentor.java @@ -61,6 +61,7 @@ public class MonasteryMentor extends CardImpl { // Prowess this.addAbility(new ProwessAbility()); + // Whenever you cast a noncreature spell, put a 1/1 white Monk creature token with prowess onto the battlefield. this.addAbility(new SpellCastControllerTriggeredAbility(new CreateTokenEffect(new MonasteryMentorToken()), filter, false)); } diff --git a/Mage.Sets/src/mage/sets/fatereforged/WhisperwoodElemental.java b/Mage.Sets/src/mage/sets/fatereforged/WhisperwoodElemental.java index 2d8174c36d9..aba6948e696 100644 --- a/Mage.Sets/src/mage/sets/fatereforged/WhisperwoodElemental.java +++ b/Mage.Sets/src/mage/sets/fatereforged/WhisperwoodElemental.java @@ -76,7 +76,7 @@ public class WhisperwoodElemental extends CardImpl { Effect effect = new GainAbilityControlledEffect(abilityToGain, Duration.EndOfTurn, filter); effect.setText("Until end of turn, face-up, nontoken creatures you control gain \"When this creature dies, manifest the top card of your library.\""); this.addAbility(new SimpleActivatedAbility( - Zone.BATTLEFIELD, effect, new SacrificeSourceCost())); + Zone.ALL, effect, new SacrificeSourceCost())); } public WhisperwoodElemental(final WhisperwoodElemental card) { diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/Misdirection.java b/Mage.Sets/src/mage/sets/mercadianmasques/Misdirection.java index 092c9a97f51..094470b2e15 100644 --- a/Mage.Sets/src/mage/sets/mercadianmasques/Misdirection.java +++ b/Mage.Sets/src/mage/sets/mercadianmasques/Misdirection.java @@ -60,10 +60,10 @@ public class Misdirection extends CardImpl { super(ownerId, 87, "Misdirection", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{3}{U}{U}"); this.expansionSetCode = "MMQ"; - // You may exile a blue card from your hand rather than pay Misdirection's mana cost. FilterOwnedCard filterCardInHand = new FilterOwnedCard("a blue card from your hand"); filterCardInHand.add(new ColorPredicate(ObjectColor.BLUE)); + // the exile cost can never be paid with the card itself filterCardInHand.add(Predicates.not(new CardIdPredicate(this.getId()))); this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filterCardInHand)))); diff --git a/Mage.Sets/src/mage/sets/visions/CityOfSolitude.java b/Mage.Sets/src/mage/sets/visions/CityOfSolitude.java index 2bac394e602..2b246421ef1 100644 --- a/Mage.Sets/src/mage/sets/visions/CityOfSolitude.java +++ b/Mage.Sets/src/mage/sets/visions/CityOfSolitude.java @@ -51,7 +51,6 @@ public class CityOfSolitude extends CardImpl { super(ownerId, 52, "City of Solitude", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}"); this.expansionSetCode = "VIS"; - // Players can cast spells and activate abilities only during their own turns. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CityOfSolitudeEffect())); } @@ -87,11 +86,6 @@ class CityOfSolitudeEffect extends ContinuousRuleModifyingEffectImpl { return !game.getActivePlayerId().equals(event.getPlayerId()); } - @Override - public boolean apply(Game game, Ability source) { - return true; - } - @Override public CityOfSolitudeEffect copy() { return new CityOfSolitudeEffect(this); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java index 775d820ec24..f980b8be196 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/ManifestTest.java @@ -362,4 +362,40 @@ public class ManifestTest extends CardTestPlayerBase { assertPermanentCount(playerB, "", 1); } + + /** + * Whisperwood Elemental - Its sacrifice ability doesn't work.. + * + */ + @Test + public void testWhisperwoodElemental() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + // Seismic Rupture deals 2 damage to each creature without flying. + addCard(Zone.HAND, playerA, "Seismic Rupture", 1); + + // At the beginning of your end step, manifest the top card of your library. + // Sacrifice Whisperwood Elemental: Until end of turn, face-up, nontoken creatures you control gain "When this creature dies, manifest the top card of your library." + addCard(Zone.BATTLEFIELD, playerB, "Whisperwood Elemental", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 2); + + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Sacrifice"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Seismic Rupture"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + // no life gain + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Seismic Rupture", 1); + assertGraveyardCount(playerB, "Whisperwood Elemental", 1); + assertGraveyardCount(playerB, "Silvercoat Lion", 2); + + assertPermanentCount(playerB, "", 2); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/TargetOpponentGainsControlTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/TargetOpponentGainsControlTest.java index 327079c36d2..9665a8c649e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/control/TargetOpponentGainsControlTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/TargetOpponentGainsControlTest.java @@ -31,14 +31,20 @@ public class TargetOpponentGainsControlTest extends CardTestPlayerBase { @Test public void testChangeControlEffectFromTwoCards() { addCard(Zone.HAND, playerA, "Lightning Bolt", 3); - addCard(Zone.HAND, playerA, "Unhallowed Pact", 3); - addCard(Zone.BATTLEFIELD, playerA, "Treacherous Pit-Dweller"); + // Enchant creature + // When enchanted creature dies, return that card to the battlefield under your control. + addCard(Zone.HAND, playerA, "Unhallowed Pact", 1); // {2}{B} + // Undying + // When Treacherous Pit-Dweller enters the battlefield from a graveyard, target opponent gains control of it. + addCard(Zone.BATTLEFIELD, playerA, "Treacherous Pit-Dweller"); // 4/3 addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unhallowed Pact", "Treacherous Pit-Dweller"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); + castSpell(1, PhaseStep.UPKEEP, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); // comes back with undying + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Unhallowed Pact", "Treacherous Pit-Dweller"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); // Treacherous Pit-Dweller is now 5/4 castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", "Treacherous Pit-Dweller"); setStopAt(1, PhaseStep.END_TURN); @@ -46,8 +52,10 @@ public class TargetOpponentGainsControlTest extends CardTestPlayerBase { // went to graveyard assertGraveyardCount(playerA, "Unhallowed Pact", 1); + assertGraveyardCount(playerA, "Lightning Bolt", 3); // returned back - assertPermanentCount(playerA, "Treacherous Pit-Dweller", 1); + assertGraveyardCount(playerA, "Treacherous Pit-Dweller", 0); + assertPermanentCount(playerB, "Treacherous Pit-Dweller", 1); // opponent gets it because ETB of Dweller resolves always last } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/MisdirectionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/MisdirectionTest.java index 737f8cd5f82..bbb375a2ccb 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/single/MisdirectionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/MisdirectionTest.java @@ -68,6 +68,74 @@ public class MisdirectionTest extends CardTestPlayerBase { assertGraveyardCount(playerA, "Rakshasa's Secret", 1); assertGraveyardCount(playerB, "Misdirection", 1); assertHandCount(playerB, "Silvercoat Lion", 0); - } + + // check to change target permanent creature legal to to a creature the opponent of the spell controller controls + @Test + public void testChangePublicExecution() { + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. + addCard(Zone.HAND, playerA, "Public Execution"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + /* + Misdirection {3}{U}{U} + Instant + You may exile a blue card from your hand rather than pay Misdirection's mana cost. + Change the target of target spell with a single target. + */ + addCard(Zone.HAND, playerB, "Misdirection"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); + addCard(Zone.BATTLEFIELD, playerB, "Custodian of the Trove", 1); // 4/3 + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Public Execution", "Pillarfield Ox"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Misdirection", "Public Execution", "Public Execution"); + addTarget(playerB, "Custodian of the Trove"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Public Execution", 1); + assertGraveyardCount(playerB, "Misdirection", 1); + + assertGraveyardCount(playerB, "Custodian of the Trove",1); + assertPermanentCount(playerB, "Pillarfield Ox", 1); + assertPowerToughness(playerB, "Pillarfield Ox", 0, 4); + + } + + // check to change target permanent creature not legal to to a creature the your opponent controls + @Test + public void testChangePublicExecution2() { + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. + addCard(Zone.HAND, playerA, "Public Execution"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + addCard(Zone.BATTLEFIELD, playerA, "Keeper of the Lens", 1); + /* + Misdirection {3}{U}{U} + Instant + You may exile a blue card from your hand rather than pay Misdirection's mana cost. + Change the target of target spell with a single target. + */ + addCard(Zone.HAND, playerB, "Misdirection"); + addCard(Zone.BATTLEFIELD, playerB, "Pillarfield Ox", 1); + addCard(Zone.BATTLEFIELD, playerB, "Custodian of the Trove", 1); // 4/3 + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Public Execution", "Custodian of the Trove"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Misdirection", "Public Execution", "Public Execution"); + addTarget(playerB, "Keeper of the Lens"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Public Execution", 1); + assertGraveyardCount(playerB, "Misdirection", 1); + assertPermanentCount(playerA, "Keeper of the Lens", 1); + + assertPermanentCount(playerB, "Pillarfield Ox", 1); + assertPowerToughness(playerB, "Pillarfield Ox", 0, 4); + + assertGraveyardCount(playerB, "Custodian of the Trove",1); + + } } \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellCastTriggerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellCastTriggerTest.java index 97da7f949d1..ce7aaa7db61 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellCastTriggerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/SpellCastTriggerTest.java @@ -65,4 +65,32 @@ public class SpellCastTriggerTest extends CardTestPlayerBase { assertPowerToughness(playerA, "Sunscorch Regent", 5, 4); } -} \ No newline at end of file + /** + * Monastery Mentor triggers are causing a "rollback" error. + */ + @Test + public void testMonasteryMentor() { + // Prowess (Whenever you cast a noncreature spell, this creature gets +1/+1 until end of turn.) + // Whenever you cast a noncreature spell, put a 1/1 white Monk creature token with prowess onto the battlefield. + addCard(Zone.BATTLEFIELD, playerA, "Monastery Mentor", 1); + + addCard(Zone.HAND, playerA, "Lightning Bolt", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 14); + + assertGraveyardCount(playerA, "Lightning Bolt", 2); + assertPermanentCount(playerA, "Monk", 2); + assertPowerToughness(playerA, "Monk", 2, 2); + assertPowerToughness(playerA, "Monk", 1, 1); + + assertPowerToughness(playerA, "Monastery Mentor", 4, 4); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index e538a04b4a5..ecca9bfc4a9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -389,6 +389,10 @@ public class TestPlayer extends ComputerPlayer { @Override public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) { if (!targets.isEmpty()) { + UUID abilityControllerId = playerId; + if (target.getTargetController() != null && target.getAbilityController() != null) { + abilityControllerId = target.getAbilityController(); + } if ((target instanceof TargetPermanent) || (target instanceof TargetPermanentOrPlayer)) { for (String targetDefinition: targets) { String[] targetList = targetDefinition.split("\\^"); @@ -408,7 +412,7 @@ public class TestPlayer extends ComputerPlayer { } for (Permanent permanent : game.getBattlefield().getAllActivePermanents((FilterPermanent)target.getFilter(), game)) { if (permanent.getName().equals(targetName) || (permanent.getName()+"-"+permanent.getExpansionSetCode()).equals(targetName)) { - if (((TargetPermanent)target).canTarget(source == null ? this.getId(): source.getControllerId(), permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) { + if (((TargetPermanent)target).canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) { if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly )) { target.add(permanent.getId(), game); targetFound = true; @@ -446,7 +450,7 @@ public class TestPlayer extends ComputerPlayer { for (String targetName: targetList) { for (Card card: this.getHand().getCards(((TargetCardInHand)target).getFilter(), game)) { if (card.getName().equals(targetName) || (card.getName()+"-"+card.getExpansionSetCode()).equals(targetName)) { - if (((TargetCardInHand)target).canTarget(this.getId(), card.getId(), source, game) && !target.getTargets().contains(card.getId())) { + if (((TargetCardInHand)target).canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) { target.add(card.getId(), game); targetFound = true; break; diff --git a/Mage/src/mage/abilities/Ability.java b/Mage/src/mage/abilities/Ability.java index a4c67c4dc75..81393981ce2 100644 --- a/Mage/src/mage/abilities/Ability.java +++ b/Mage/src/mage/abilities/Ability.java @@ -537,4 +537,6 @@ public interface Ability extends Controllable, Serializable { */ MageObject getSourceObjectIfItStillExists(Game game); + + String getTargetDescription(Targets targets, Game game); } diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 50f08e26194..844e6de7c48 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -1074,6 +1074,7 @@ public abstract class AbilityImpl implements Ability { return sb.toString(); } + @Override public String getTargetDescription(Targets targets, Game game) { return getTargetDescriptionForLog(targets, game); } diff --git a/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java b/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java index ee08b2d7417..265b66e9cdd 100644 --- a/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/ChooseNewTargetsTargetEffect.java @@ -72,6 +72,7 @@ public class ChooseNewTargetsTargetEffect extends OneShotEffect { super(effect); this.forceChange = effect.forceChange; this.onlyOneTarget = effect.onlyOneTarget; + this.filterNewTarget = effect.filterNewTarget; } @Override diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index 4676f22cf88..03c01660280 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -37,39 +37,33 @@ import mage.Mana; import mage.ObjectColor; import mage.abilities.Abilities; import mage.abilities.Ability; -import mage.abilities.Mode; import mage.abilities.SpellAbility; import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.Cost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.keyword.BestowAbility; import mage.abilities.keyword.MorphAbility; import mage.cards.Card; import mage.cards.SplitCard; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SpellAbilityType; import mage.constants.Zone; import mage.counters.Counter; import mage.counters.Counters; -import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.players.Player; -import mage.target.Target; -import mage.target.TargetAmount; import mage.util.GameLog; /** * * @author BetaSteward_at_googlemail.com */ -public class Spell implements StackObject, Card { +public class Spell extends StackObjImpl implements Card { private final List spellCards = new ArrayList<>(); private final List spellAbilities = new ArrayList<>(); @@ -313,213 +307,8 @@ public class Spell implements StackObject, Card { } } } - - /** - * Choose new targets for the spell - * - * @param game - * @param playerId Player UUID who changes the targets. - * @return - */ - public boolean chooseNewTargets(Game game, UUID playerId) { - return chooseNewTargets(game, playerId, false, false, null); - } - - /** - * 114.6. Some effects allow a player to change the target(s) of a spell or - * ability, and other effects allow a player to choose new targets for a - * spell or ability. - * - * 114.6a If an effect allows a player to "change the - * target(s)" of a spell or ability, each target can be changed only to - * another legal target. If a target can't be changed to another legal - * target, the original target is unchanged, even if the original target is - * itself illegal by then. If all the targets aren't changed to other legal - * targets, none of them are changed. - * - * 114.6b If an effect allows a player to "change a target" of a - * spell or ability, the process described in rule 114.6a - * is followed, except that only one of those targets may be changed - * (rather than all of them or none of them). - * - * 114.6c If an effect allows a - * player to "change any targets" of a spell or ability, the process - * described in rule 114.6a is followed, except that any number of those - * targets may be changed (rather than all of them or none of them). - * - * 114.6d If an effect allows a player to "choose new targets" for a spell or - * ability, the player may leave any number of the targets unchanged, even - * if those targets would be illegal. If the player chooses to change some - * or all of the targets, the new targets must be legal and must not cause - * any unchanged targets to become illegal. - * - * 114.6e When changing targets or - * choosing new targets for a spell or ability, only the final set of - * targets is evaluated to determine whether the change is legal. - * - * Example: Arc Trail is a sorcery that reads "Arc Trail deals 2 damage to - * target creature or player and 1 damage to another target creature or - * player." The current targets of Arc Trail are Runeclaw Bear and Llanowar - * Elves, in that order. You cast Redirect, an instant that reads "You may - * choose new targets for target spell," targeting Arc Trail. You can change - * the first target to Llanowar Elves and change the second target to - * Runeclaw Bear. - * - * 114.7. Modal spells and abilities may have different targeting - * requirements for each mode. An effect that allows a player to change the - * target(s) of a modal spell or ability, or to choose new targets for a - * modal spell or ability, doesn't allow that player to change its mode. - * (See rule 700.2.) - * - * 706.10c Some effects copy a spell or ability and state that its - * controller may choose new targets for the copy. The player may leave any - * number of the targets unchanged, even if those targets would be illegal. - * If the player chooses to change some or all of the targets, the new - * targets must be legal. Once the player has decided what the copy's - * targets will be, the copy is put onto the stack with those targets. - * - * @param game - * @param playerId - player that can/has to change the taregt of the spell - * @param forceChange - does only work for targets with maximum of one targetId - * @param onlyOneTarget - 114.6b one target must be changed to another target - * @param filterNewTarget restriction for the new target, if null nothing is cheched - * @return - */ - @Override - public boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { - Player player = game.getPlayer(playerId); - if (player != null) { - StringBuilder newTargetDescription = new StringBuilder(); - // Fused split spells or spells where "Splice on Arcane" was used can have more than one ability - for (SpellAbility spellAbility : spellAbilities) { - // Some spells can have more than one mode - for (UUID modeId : spellAbility.getModes().getSelectedModes()) { - Mode mode = spellAbility.getModes().get(modeId); - for (Target target : mode.getTargets()) { - Target newTarget = chooseNewTarget(player, spellAbility, mode, target, forceChange, filterNewTarget, game); - // clear the old target and copy all targets from new target - target.clearChosen(); - for (UUID targetId : newTarget.getTargets()) { - target.addTarget(targetId, newTarget.getTargetAmount(targetId), spellAbility, game, false); - } - - } - newTargetDescription.append(getSpellAbility().getTargetDescription(mode.getTargets(), game)); - } - - } - if (newTargetDescription.length() > 0 && !game.isSimulation()) { - game.informPlayers(this.getName() + " is now " + newTargetDescription.toString()); - } - return true; - } - return false; - } - - /** - * Handles the change of one target instance of a mode - * - * @param player - player that can choose the new target - * @param spellAbility - * @param mode - * @param target - * @param forceChange - * @param game - * @return - */ - private Target chooseNewTarget(Player player, SpellAbility spellAbility, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) { - Target newTarget = target.copy(); - newTarget.clearChosen(); - for (UUID targetId : target.getTargets()) { - String targetNames = getNamesOftargets(targetId, game); - // change the target? - if (targetNames != null - && (forceChange || player.chooseUse(mode.getEffects().get(0).getOutcome(), "Change this target: " + targetNames + "?", game))) { - // choose exactly one other target - if (forceChange && target.possibleTargets(this.getSourceId(), getControllerId(), game).size() > 1) { // controller of spell must be used (e.g. TargetOpponent) - int iteration = 0; - do { - if (iteration > 0 && !game.isSimulation()) { - game.informPlayer(player, "You may only select exactly one target that must be different from the origin target!"); - } - iteration++; - newTarget.clearChosen(); - // TODO: Distinction between "spell controller" and "player that can change the target" - here player is used for both - newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), spellAbility, game); - // check target restriction - if (newTarget.getFirstTarget() != null && filterNewTarget != null) { - Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); - if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { - game.informPlayer(player, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); - newTarget.clearChosen(); - } - } - } while (player.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1)); - // choose a new target - } else { - // build a target definition with exactly one possible target to select that replaces old target - Target tempTarget = target.copy(); - if (target instanceof TargetAmount) { - ((TargetAmount)tempTarget).setAmountDefinition(new StaticValue(target.getTargetAmount(targetId))); - } - tempTarget.setMinNumberOfTargets(1); - tempTarget.setMaxNumberOfTargets(1); - boolean again; - do { - again = false; - tempTarget.clearChosen(); - if (!tempTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), spellAbility, game)) { - if (player.chooseUse(Outcome.Benefit, "No target object selected. Reset to original target?", game)) { - // use previous target no target was selected - newTarget.addTarget(targetId, target.getTargetAmount(targetId), spellAbility, game, false); - } else { - again = true; - } - } else { - // if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition - if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) { - if(player.isHuman()) { - game.informPlayer(player, "This target was already selected from origin spell. You can only keep this target!"); - again = true; - } else { - newTarget.addTarget(targetId, target.getTargetAmount(targetId), spellAbility, game, false); - } - } else if (newTarget.getFirstTarget() != null && filterNewTarget != null) { - Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); - if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { - game.informPlayer(player, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); - again = true; - } - } else { - // valid target was selected, add it to the new target definition - newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), spellAbility, game, false); - } - } - } while (again && player.isInGame()); - } - } - // keep the target - else { - newTarget.addTarget(targetId, target.getTargetAmount(targetId), spellAbility, game, false); - } - } - return newTarget; - } - - private String getNamesOftargets(UUID targetId, Game game) { - MageObject object = game.getObject(targetId); - String name = null; - if (object == null) { - Player targetPlayer = game.getPlayer(targetId); - if (targetPlayer != null) { - name = targetPlayer.getLogName(); - } - } else { - name = object.getName(); - } - return name; - } + @Override public void counter(UUID sourceId, Game game) { diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index 29804bbf9bc..835d8bf1014 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -54,15 +54,10 @@ import mage.target.Targets; import java.util.ArrayList; import java.util.List; import java.util.UUID; -import mage.abilities.dynamicvalue.common.StaticValue; import mage.cards.Card; import mage.constants.AbilityWord; -import mage.constants.Outcome; -import mage.filter.FilterPermanent; import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.TargetAmount; import mage.util.GameLog; import mage.watchers.Watcher; @@ -70,7 +65,7 @@ import mage.watchers.Watcher; * * @author BetaSteward_at_googlemail.com */ -public class StackAbility implements StackObject, Ability { +public class StackAbility extends StackObjImpl implements Ability { private static List emptyCardType = new ArrayList<>(); private static List emptyString = new ArrayList<>(); @@ -551,197 +546,9 @@ public class StackAbility implements StackObject, Ability { throw new UnsupportedOperationException("Not supported."); } - /** - * 114.6. Some effects allow a player to change the target(s) of a spell or - * ability, and other effects allow a player to choose new targets for a - * spell or ability. - * - * 114.6a If an effect allows a player to "change the - * target(s)" of a spell or ability, each target can be changed only to - * another legal target. If a target can't be changed to another legal - * target, the original target is unchanged, even if the original target is - * itself illegal by then. If all the targets aren't changed to other legal - * targets, none of them are changed. - * - * 114.6b If an effect allows a player to "change a target" of a - * spell or ability, the process described in rule 114.6a - * is followed, except that only one of those targets may be changed - * (rather than all of them or none of them). - * - * 114.6c If an effect allows a - * player to "change any targets" of a spell or ability, the process - * described in rule 114.6a is followed, except that any number of those - * targets may be changed (rather than all of them or none of them). - * - * 114.6d If an effect allows a player to "choose new targets" for a spell or - * ability, the player may leave any number of the targets unchanged, even - * if those targets would be illegal. If the player chooses to change some - * or all of the targets, the new targets must be legal and must not cause - * any unchanged targets to become illegal. - * - * 114.6e When changing targets or - * choosing new targets for a spell or ability, only the final set of - * targets is evaluated to determine whether the change is legal. - * - * Example: Arc Trail is a sorcery that reads "Arc Trail deals 2 damage to - * target creature or player and 1 damage to another target creature or - * player." The current targets of Arc Trail are Runeclaw Bear and Llanowar - * Elves, in that order. You cast Redirect, an instant that reads "You may - * choose new targets for target spell," targeting Arc Trail. You can change - * the first target to Llanowar Elves and change the second target to - * Runeclaw Bear. - * - * 114.7. Modal spells and abilities may have different targeting - * requirements for each mode. An effect that allows a player to change the - * target(s) of a modal spell or ability, or to choose new targets for a - * modal spell or ability, doesn't allow that player to change its mode. - * (See rule 700.2.) - * - * 706.10c Some effects copy a spell or ability and state that its - * controller may choose new targets for the copy. The player may leave any - * number of the targets unchanged, even if those targets would be illegal. - * If the player chooses to change some or all of the targets, the new - * targets must be legal. Once the player has decided what the copy's - * targets will be, the copy is put onto the stack with those targets. - * - * @param game - * @param playerId - player that can/has to change the target of the ability - * @param forceChange - does only work for targets with maximum of one targetId - * @param onlyOneTarget - 114.6b one target must be changed to another target - * @param filterNewTarget restriction for the new target, if null nothing is cheched - * @return - */ @Override - public boolean chooseNewTargets(Game game, UUID playerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { - Player player = game.getPlayer(playerId); - if (player != null) { - StringBuilder newTargetDescription = new StringBuilder(); - // Some abilities can have more than one mode - for (UUID modeId : ability.getModes().getSelectedModes()) { - Mode mode = ability.getModes().get(modeId); - for (Target target : mode.getTargets()) { - Target newTarget = chooseNewTarget(player, getStackAbility(), mode, target, forceChange, filterNewTarget, game); - // clear the old target and copy all targets from new target - target.clearChosen(); - for (UUID targetId : newTarget.getTargets()) { - target.addTarget(targetId, newTarget.getTargetAmount(targetId), ability, game, false); - } - - } - newTargetDescription.append(((AbilityImpl)ability).getTargetDescription(mode.getTargets(), game)); - } - - if (newTargetDescription.length() > 0 && !game.isSimulation()) { - game.informPlayers(this.getName() + " is now " + newTargetDescription.toString()); - } - return true; - } - return false; + public String getTargetDescription(Targets targets, Game game) { + return getAbilities().get(0).getTargetDescription(targets, game); } - /** - * Handles the change of one target instance of a mode - * - * @param player - player that can choose the new target - * @param ability - * @param mode - * @param target - * @param forceChange - * @param game - * @return - */ - private Target chooseNewTarget(Player player, Ability ability, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) { - Target newTarget = target.copy(); - newTarget.clearChosen(); - for (UUID targetId : target.getTargets()) { - String targetNames = getNamesOfTargets(targetId, game); - // change the target? - if (targetNames != null - && (forceChange || player.chooseUse(mode.getEffects().get(0).getOutcome(), "Change this target: " + targetNames + "?", game))) { - // choose exactly one other target - if (forceChange && target.possibleTargets(this.getSourceId(), getControllerId(), game).size() > 1) { // controller of ability must be used (e.g. TargetOpponent) - int iteration = 0; - do { - if (iteration > 0 && !game.isSimulation()) { - game.informPlayer(player, "You may only select exactly one target that must be different from the origin target!"); - } - iteration++; - newTarget.clearChosen(); - // TODO: Distinction between "ability controller" and "player that can change the target" - here player is used for both - newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), ability, game); - // check target restriction - if (newTarget.getFirstTarget() != null && filterNewTarget != null) { - Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); - if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { - game.informPlayer(player, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); - newTarget.clearChosen(); - } - } - } while (player.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1)); - // choose a new target - } else { - // build a target definition with exactly one possible target to select that replaces old target - Target tempTarget = target.copy(); - if (target instanceof TargetAmount) { - ((TargetAmount)tempTarget).setAmountDefinition(new StaticValue(target.getTargetAmount(targetId))); - } - tempTarget.setMinNumberOfTargets(1); - tempTarget.setMaxNumberOfTargets(1); - boolean again; - do { - again = false; - tempTarget.clearChosen(); - if (!tempTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), player.getId(), ability, game)) { - if (player.chooseUse(Outcome.Benefit, "No target object selected. Reset to original target?", game)) { - // use previous target no target was selected - newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); - } else { - again = true; - } - } else { - // if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition - if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) { - if (player.isHuman()) { - game.informPlayer(player, "This target was already selected from origin ability. You can only keep this target!"); - again = true; - } else { - newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); - } - } else if (newTarget.getFirstTarget() != null && filterNewTarget != null) { - Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); - if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { - game.informPlayer(player, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); - again = true; - } - } else { - // valid target was selected, add it to the new target definition - newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false); - } - } - } while (again && player.isInGame()); - } - } - // keep the target - else { - newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); - } - } - return newTarget; - } - - - private String getNamesOfTargets(UUID targetId, Game game) { - MageObject object = game.getObject(targetId); - String targetNames = null; - if (object == null) { - Player targetPlayer = game.getPlayer(targetId); - if (targetPlayer != null) { - targetNames = targetPlayer.getLogName(); - } - } else { - targetNames = object.getName(); - } - return targetNames; - } - } diff --git a/Mage/src/mage/game/stack/StackObjImpl.java b/Mage/src/mage/game/stack/StackObjImpl.java new file mode 100644 index 00000000000..c4889dc1c90 --- /dev/null +++ b/Mage/src/mage/game/stack/StackObjImpl.java @@ -0,0 +1,260 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.game.stack; + +import java.util.Set; +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Abilities; +import mage.abilities.AbilitiesImpl; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.constants.Outcome; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.Target; +import mage.target.TargetAmount; + +/** + * + * @author LevelX2 + */ +public abstract class StackObjImpl implements StackObject { + + /** + * Choose new targets for a stack Object + * + * @param game + * @param playerId Player UUID who changes the targets. + * @return + */ + public boolean chooseNewTargets(Game game, UUID playerId) { + return chooseNewTargets(game, playerId, false, false, null); + } + + /** + * 114.6. Some effects allow a player to change the target(s) of a spell or + * ability, and other effects allow a player to choose new targets for a + * spell or ability. + * + * 114.6a If an effect allows a player to "change the + * target(s)" of a spell or ability, each target can be changed only to + * another legal target. If a target can't be changed to another legal + * target, the original target is unchanged, even if the original target is + * itself illegal by then. If all the targets aren't changed to other legal + * targets, none of them are changed. + * + * 114.6b If an effect allows a player to "change a target" of a + * spell or ability, the process described in rule 114.6a + * is followed, except that only one of those targets may be changed + * (rather than all of them or none of them). + * + * 114.6c If an effect allows a + * player to "change any targets" of a spell or ability, the process + * described in rule 114.6a is followed, except that any number of those + * targets may be changed (rather than all of them or none of them). + * + * 114.6d If an effect allows a player to "choose new targets" for a spell or + * ability, the player may leave any number of the targets unchanged, even + * if those targets would be illegal. If the player chooses to change some + * or all of the targets, the new targets must be legal and must not cause + * any unchanged targets to become illegal. + * + * 114.6e When changing targets or + * choosing new targets for a spell or ability, only the final set of + * targets is evaluated to determine whether the change is legal. + * + * Example: Arc Trail is a sorcery that reads "Arc Trail deals 2 damage to + * target creature or player and 1 damage to another target creature or + * player." The current targets of Arc Trail are Runeclaw Bear and Llanowar + * Elves, in that order. You cast Redirect, an instant that reads "You may + * choose new targets for target spell," targeting Arc Trail. You can change + * the first target to Llanowar Elves and change the second target to + * Runeclaw Bear. + * + * 114.7. Modal spells and abilities may have different targeting + * requirements for each mode. An effect that allows a player to change the + * target(s) of a modal spell or ability, or to choose new targets for a + * modal spell or ability, doesn't allow that player to change its mode. + * (See rule 700.2.) + * + * 706.10c Some effects copy a spell or ability and state that its + * controller may choose new targets for the copy. The player may leave any + * number of the targets unchanged, even if those targets would be illegal. + * If the player chooses to change some or all of the targets, the new + * targets must be legal. Once the player has decided what the copy's + * targets will be, the copy is put onto the stack with those targets. + * + * @param game + * @param targetControllerId - player that can/has to change the target of the spell + * @param forceChange - does only work for targets with maximum of one targetId + * @param onlyOneTarget - 114.6b one target must be changed to another target + * @param filterNewTarget restriction for the new target, if null nothing is cheched + * @return + */ + @Override + public boolean chooseNewTargets(Game game, UUID targetControllerId, boolean forceChange, boolean onlyOneTarget, FilterPermanent filterNewTarget) { + Player targetController = game.getPlayer(targetControllerId); + if (targetController != null) { + StringBuilder newTargetDescription = new StringBuilder(); + // Fused split spells or spells where "Splice on Arcane" was used can have more than one ability + Abilities objectAbilities = new AbilitiesImpl<>(); + if (this instanceof Spell) { + objectAbilities.addAll(((Spell)this).getSpellAbilities()); + } else { + objectAbilities.addAll(getAbilities()); + } + for (Ability ability : objectAbilities) { + // Some spells can have more than one mode + for (UUID modeId : ability.getModes().getSelectedModes()) { + Mode mode = ability.getModes().get(modeId); + for (Target target : mode.getTargets()) { + Target newTarget = chooseNewTarget(targetController, ability, mode, target, forceChange, filterNewTarget, game); + // clear the old target and copy all targets from new target + target.clearChosen(); + for (UUID targetId : newTarget.getTargets()) { + target.addTarget(targetId, newTarget.getTargetAmount(targetId), ability, game, false); + } + + } + newTargetDescription.append(ability.getTargetDescription(mode.getTargets(), game)); + } + + } + if (newTargetDescription.length() > 0 && !game.isSimulation()) { + game.informPlayers(this.getLogName() + " is now " + newTargetDescription.toString()); + } + return true; + } + return false; + } + + /** + * Handles the change of one target instance of a mode + * + * @param targetController - player that can choose the new target + * @param ability + * @param mode + * @param target + * @param forceChange + * @param game + * @return + */ + private Target chooseNewTarget(Player targetController, Ability ability, Mode mode, Target target, boolean forceChange, FilterPermanent filterNewTarget, Game game) { + Target newTarget = target.copy(); + if (!targetController.getId().equals(getControllerId())) { + newTarget.setTargetController(targetController.getId()); // target controller for the change is different from spell controller + newTarget.setAbilityController(getControllerId()); + } + newTarget.clearChosen(); + for (UUID targetId : target.getTargets()) { + String targetNames = getNamesOftargets(targetId, game); + // change the target? + if (targetNames != null + && (forceChange || targetController.chooseUse(mode.getEffects().get(0).getOutcome(), "Change this target: " + targetNames + "?", game))) { + Set possibleTargets = target.possibleTargets(this.getSourceId(), getControllerId(), game); + // choose exactly one other target - already targeted objects are not counted + if (forceChange && possibleTargets != null && possibleTargets.size() > 1) { // controller of spell must be used (e.g. TargetOpponent) + int iteration = 0; + do { + if (iteration > 0 && !game.isSimulation()) { + game.informPlayer(targetController, "You may only select exactly one target that must be different from the origin target!"); + } + iteration++; + newTarget.clearChosen(); + + newTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), getControllerId(), ability, game); + // check target restriction + if (newTarget.getFirstTarget() != null && filterNewTarget != null) { + Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); + if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { + game.informPlayer(targetController, "Target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); + newTarget.clearChosen(); + } + } + } while (targetController.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1)); + // choose a new target + } else { + // build a target definition with exactly one possible target to select that replaces old target + Target tempTarget = target.copy(); + if (target instanceof TargetAmount) { + ((TargetAmount)tempTarget).setAmountDefinition(new StaticValue(target.getTargetAmount(targetId))); + } + tempTarget.setMinNumberOfTargets(1); + tempTarget.setMaxNumberOfTargets(1); + if (!targetController.getId().equals(getControllerId())) { + tempTarget.setTargetController(targetController.getId()); + tempTarget.setAbilityController(getControllerId()); + } + boolean again; + do { + again = false; + tempTarget.clearChosen(); + if (!tempTarget.chooseTarget(mode.getEffects().get(0).getOutcome(), getControllerId(), ability, game)) { + if (targetController.chooseUse(Outcome.Benefit, "No target object selected. Reset to original target?", game)) { + // use previous target no target was selected + newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + } else { + again = true; + } + } else { + // if possible add the alternate Target - it may not be included in the old definition nor in the already selected targets of the new definition + if (newTarget.getTargets().contains(tempTarget.getFirstTarget()) || target.getTargets().contains(tempTarget.getFirstTarget())) { + if(targetController.isHuman()) { + game.informPlayer(targetController, "This target was already selected from origin spell. You can only keep this target!"); + again = true; + } else { + newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + } + } else if (!target.canTarget(getControllerId(), tempTarget.getFirstTarget(), ability, game)) { + if(targetController.isHuman()) { + game.informPlayer(targetController, "This target is not valid!"); + again = true; + } else { + // keep the old + newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + } + } else if (newTarget.getFirstTarget() != null && filterNewTarget != null) { + Permanent newTargetPermanent = game.getPermanent(newTarget.getFirstTarget()); + if (newTargetPermanent == null || !filterNewTarget.match(newTargetPermanent, game)) { + game.informPlayer(targetController, "This target does not fullfil the target requirements (" + filterNewTarget.getMessage() +")"); + again = true; + } + } else { + // valid target was selected, add it to the new target definition + newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false); + } + } + } while (again && targetController.isInGame()); + } + } + // keep the target + else { + newTarget.addTarget(targetId, target.getTargetAmount(targetId), ability, game, false); + } + } + return newTarget; + } + + + private String getNamesOftargets(UUID targetId, Game game) { + MageObject object = game.getObject(targetId); + String name = null; + if (object == null) { + Player targetPlayer = game.getPlayer(targetId); + if (targetPlayer != null) { + name = targetPlayer.getLogName(); + } + } else { + name = object.getName(); + } + return name; + } + +} diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index ef6a0004790..16ce833b928 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -130,7 +130,6 @@ import mage.target.common.TargetCardInLibrary; import mage.target.common.TargetDiscard; import mage.util.CardUtil; import mage.util.GameLog; -import mage.watchers.common.BloodthirstWatcher; import org.apache.log4j.Logger; @@ -690,7 +689,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public Cards discard(int amount, boolean random, Ability source, Game game) { Cards discardedCards = new CardsImpl(); - if (amount >= this.getHand().size()) { + if (this.getHand().size() == 1) { discardedCards.addAll(this.getHand()); while (this.getHand().size() > 0) { discard(this.getHand().get(this.getHand().iterator().next(), game), source, game); diff --git a/Mage/src/mage/target/Target.java b/Mage/src/mage/target/Target.java index c1c5ea41846..0ac5a3c43ba 100644 --- a/Mage/src/mage/target/Target.java +++ b/Mage/src/mage/target/Target.java @@ -38,6 +38,7 @@ import java.io.Serializable; import java.util.List; import java.util.Set; import java.util.UUID; +import mage.players.Player; /** * @@ -108,4 +109,7 @@ public interface Target extends Serializable { // some targets are choosen from players that are not the controller of the ability (e.g. Pandemonium) void setTargetController(UUID playerId); UUID getTargetController(); + void setAbilityController(UUID playerId); + UUID getAbilityController(); + Player getTargetController(Game game, UUID playerId); } diff --git a/Mage/src/mage/target/TargetAmount.java b/Mage/src/mage/target/TargetAmount.java index e9060539e22..36cbb56ebc4 100644 --- a/Mage/src/mage/target/TargetAmount.java +++ b/Mage/src/mage/target/TargetAmount.java @@ -117,10 +117,9 @@ public abstract class TargetAmount extends TargetImpl { if (!amountWasSet) { setAmount(source, game); } - Player player = game.getPlayer(playerId); chosen = remainingAmount == 0; while (remainingAmount > 0) { - if (!player.chooseTargetAmount(outcome, this, source, game)) { + if (!getTargetController(game, playerId).chooseTargetAmount(outcome, this, source, game)) { return chosen; } chosen = remainingAmount == 0; diff --git a/Mage/src/mage/target/TargetImpl.java b/Mage/src/mage/target/TargetImpl.java index c6d46a94c66..ab3eb6ef68e 100644 --- a/Mage/src/mage/target/TargetImpl.java +++ b/Mage/src/mage/target/TargetImpl.java @@ -61,6 +61,7 @@ public abstract class TargetImpl implements Target { protected boolean notTarget = false; protected boolean atRandom = false; protected UUID targetController = null; // if null the ability controller is the targetController + protected UUID abilityController = null; // only used if target controller != ability controller @Override public abstract TargetImpl copy(); @@ -86,6 +87,7 @@ public abstract class TargetImpl implements Target { this.atRandom = target.atRandom; this.notTarget = target.notTarget; this.targetController = target.targetController; + this.abilityController = target.abilityController; } @Override @@ -110,6 +112,12 @@ public abstract class TargetImpl implements Target { @Override public String getMessage() { + String suffix = ""; + if (targetController != null) { + // Hint for the selecting player that the targets must be valid from the point of the ability controller + // e.g. select opponent text may be misleading otherwise + suffix = " (target controlling!)"; + } if (getMaxNumberOfTargets() != 1) { StringBuilder sb = new StringBuilder(); sb.append("Select ").append(targetName); @@ -118,12 +126,13 @@ public abstract class TargetImpl implements Target { } else { sb.append(" (").append(targets.size()).append(")"); } + sb.append(suffix); return sb.toString(); } if (targetName.startsWith("another") || targetName.startsWith("a ") || targetName.startsWith("an ")) { - return "Select " + targetName; + return "Select " + targetName + suffix; } - return "Select a " + targetName; + return "Select a " + targetName + suffix; } @Override @@ -298,7 +307,6 @@ public abstract class TargetImpl implements Target { @Override public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) { - Player player = game.getPlayer(playerId); while (!isChosen() && !doneChosing()) { chosen = targets.size() >= getNumberOfTargets(); if (isRandom()) { @@ -316,7 +324,7 @@ public abstract class TargetImpl implements Target { return chosen; } } else { - if (!player.chooseTarget(outcome, this, source, game)) { + if (!getTargetController(game, playerId).chooseTarget(outcome, this, source, game)) { return chosen; } } @@ -433,5 +441,23 @@ public abstract class TargetImpl implements Target { return targetController; } + @Override + public void setAbilityController(UUID playerId) { + this.abilityController = playerId; + } + @Override + public UUID getAbilityController() { + return abilityController; + } + + @Override + public Player getTargetController(Game game, UUID playerId) { + if (getTargetController() != null) { + return game.getPlayer(getTargetController()); + } else { + return game.getPlayer(playerId); + } + } + } From bc21b8d475bc7bbca349aa8589c82d0afb3deaec Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 31 May 2015 19:17:15 +0200 Subject: [PATCH 021/102] * Deathmist Raptor - Fixed that the return ability did wrongly also work if the Raptor had left the graveyard. --- .../sets/dragonsoftarkir/DeathmistRaptor.java | 11 ++--- ...va => ReturnToBattlefieldEffectsTest.java} | 40 ++++++++++++++++++- 2 files changed, 44 insertions(+), 7 deletions(-) rename Mage.Tests/src/test/java/org/mage/test/cards/triggers/{ReturnToBattlefieldUnderYourControlTargetEffectTest.java => ReturnToBattlefieldEffectsTest.java} (71%) diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/DeathmistRaptor.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/DeathmistRaptor.java index ffde7596bcb..d6295186c56 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/DeathmistRaptor.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/DeathmistRaptor.java @@ -29,6 +29,7 @@ package mage.sets.dragonsoftarkir; import java.util.UUID; import mage.MageInt; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.TurnedFaceUpAllTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -63,7 +64,7 @@ public class DeathmistRaptor extends CardImpl { this.addAbility(DeathtouchAbility.getInstance()); // Whenever a permanent you control is turned face up, you may return Deathmist Raptor from your graveyard to the battlefield face up or face down. - this.addAbility(new TurnedFaceUpAllTriggeredAbility(Zone.GRAVEYARD, new DeathmistRaptorEffect(), new FilterControlledPermanent(), false, true)); + this.addAbility(new TurnedFaceUpAllTriggeredAbility(Zone.GRAVEYARD, new DeathmistRaptorEffect(), new FilterControlledPermanent("a permanent you control"), false, true)); // Megamorph {4}{G} this.addAbility(new MorphAbility(this, new ManaCostsImpl("{4}{G}"), true)); @@ -98,10 +99,10 @@ class DeathmistRaptorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Card card = game.getCard(source.getSourceId()); - if (controller != null && card != null) { - controller.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId(), false, - controller.chooseUse(Outcome.Detriment, "Return " + card.getName() + " face down to battlefield (otherwise face up)?", game)); + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (controller != null && (sourceObject instanceof Card)) { + controller.putOntoBattlefieldWithInfo((Card) sourceObject, game, Zone.GRAVEYARD, source.getSourceId(), false, + controller.chooseUse(Outcome.Detriment, "Return " + sourceObject.getLogName() + " face down to battlefield (otherwise face up)?", game)); return true; } return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldUnderYourControlTargetEffectTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldEffectsTest.java similarity index 71% rename from Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldUnderYourControlTargetEffectTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldEffectsTest.java index fee25268faf..dc6b5f32a59 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldUnderYourControlTargetEffectTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToBattlefieldEffectsTest.java @@ -37,7 +37,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * @author LevelX2 */ -public class ReturnToBattlefieldUnderYourControlTargetEffectTest extends CardTestPlayerBase { +public class ReturnToBattlefieldEffectsTest extends CardTestPlayerBase { @Test public void testSaffiEriksdotter() { @@ -115,5 +115,41 @@ public class ReturnToBattlefieldUnderYourControlTargetEffectTest extends CardTes assertExileCount("Arcbound Worker", 1); } - + /** + * With my opponent's Deathmist Raptor return-to-battlefield trigger on the stack, + * I exiled the Deathmist Raptor with Pharika, God of Affliction. However, the Deathmist Raptor + * returned to the battlefield from exile, though it should not have because it had + * changed zones so was a different object. + */ + @Test + public void testDeathmistRaptor() { + // Deathtouch + // Whenever a permanent you control is turned face up, you may return Deathmist Raptor from your graveyard to the battlefield face up or face down. + // Megamorph {4}{G} + addCard(Zone.GRAVEYARD, playerA, "Deathmist Raptor"); + addCard(Zone.HAND, playerA, "Pine Walker"); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + + addCard(Zone.BATTLEFIELD, playerB, "Forest", 1); + addCard(Zone.BATTLEFIELD, playerB, "Swamp", 1); + // {B}{G}: Exile target creature card from a graveyard. It's owner puts a 1/1 black and green Snake enchantment creature token with deathtouch onto the battlefield. + addCard(Zone.BATTLEFIELD, playerB, "Pharika, God of Affliction", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker"); + setChoice(playerA, "Yes"); // cast it face down as 2/2 creature + + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "{4}{G}: Turn this face-down permanent face up."); + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerB, "{B}{G}: Exile target creature card from a graveyard", + "Deathmist Raptor", "Whenever a permanent you control is turned face up"); + + setStopAt(3, PhaseStep.END_TURN); + + execute(); + assertPermanentCount(playerB, "Pharika, God of Affliction", 1); + assertPermanentCount(playerA, "Snake", 1); + assertPermanentCount(playerA, "Pine Walker", 1); + + assertExileCount("Deathmist Raptor", 1); + + } } \ No newline at end of file From c99fb3b1781eb5c42acf4e1e5fae8a7a23e25943 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 31 May 2015 19:37:59 +0200 Subject: [PATCH 022/102] * Possibility Storm - Fixed that if the card that shares a card type could not be cast (because it's a land or there were no valid targets) causing java error if the player confirmed he wants to cast it. --- Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java b/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java index 24f21aa767a..0b751240674 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/PossibilityStorm.java @@ -54,7 +54,6 @@ import mage.target.targetpointer.FixedTarget; * @author LevelX2 */ - public class PossibilityStorm extends CardImpl { public PossibilityStorm(UUID ownerId) { @@ -123,7 +122,7 @@ class PossibilityStormEffect extends OneShotEffect { public PossibilityStormEffect() { super(Outcome.Neutral); - staticText = "that player exiles it, then exiles cards from the top of his or her library until he or she exiles a card that shares a card type with it. That player may cast that card without paying its mana cost. Then he or she puts all cards exiled with Possibility Storm on the bottom of his or her library in a random order"; + staticText = "that player exiles it, then exiles cards from the top of his or her library until he or she exiles a card that shares a card type with it. That player may cast that card without paying its mana cost. Then he or she puts all cards exiled with {this} on the bottom of his or her library in a random order"; } public PossibilityStormEffect(final PossibilityStormEffect effect) { @@ -148,7 +147,9 @@ class PossibilityStormEffect extends OneShotEffect { } } while (library.size() > 0 && card != null && !sharesType(card, spell.getCardType())); - if (card != null && sharesType(card, spell.getCardType())) { + if (card != null && sharesType(card, spell.getCardType()) && + !card.getCardType().contains(CardType.LAND) && + card.getSpellAbility().getTargets().canChoose(spellController.getId(), game)) { if (spellController.chooseUse(Outcome.PlayForFree, "Cast " + card.getLogName() + " without paying cost?", game)) { spellController.cast(card.getSpellAbility(), game, true); } From 413fc1ce67adbb0caaf03e675483bcf33e854c2d Mon Sep 17 00:00:00 2001 From: emerald000 Date: Mon, 1 Jun 2015 00:32:23 -0400 Subject: [PATCH 023/102] Added Pulse of the Fields --- .../mage/sets/darksteel/PulseOfTheFields.java | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/darksteel/PulseOfTheFields.java diff --git a/Mage.Sets/src/mage/sets/darksteel/PulseOfTheFields.java b/Mage.Sets/src/mage/sets/darksteel/PulseOfTheFields.java new file mode 100644 index 00000000000..49191523e34 --- /dev/null +++ b/Mage.Sets/src/mage/sets/darksteel/PulseOfTheFields.java @@ -0,0 +1,99 @@ +/* + * 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 mage.sets.darksteel; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.GainLifeEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author emerald000 + */ +public class PulseOfTheFields extends CardImpl { + + public PulseOfTheFields(UUID ownerId) { + super(ownerId, 11, "Pulse of the Fields", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{1}{W}{W}"); + this.expansionSetCode = "DST"; + + // You gain 4 life. Then if an opponent has more life than you, return Pulse of the Fields to its owner's hand. + this.getSpellAbility().addEffect(new GainLifeEffect(4)); + this.getSpellAbility().addEffect(new PulseOfTheFieldsReturnToHandEffect()); + } + + public PulseOfTheFields(final PulseOfTheFields card) { + super(card); + } + + @Override + public PulseOfTheFields copy() { + return new PulseOfTheFields(this); + } +} + +class PulseOfTheFieldsReturnToHandEffect extends OneShotEffect { + + PulseOfTheFieldsReturnToHandEffect() { + super(Outcome.Benefit); + this.staticText = "Then if an opponent has more life than you, return {this} to its owner's hand"; + } + + PulseOfTheFieldsReturnToHandEffect(final PulseOfTheFieldsReturnToHandEffect effect) { + super(effect); + } + + @Override + public PulseOfTheFieldsReturnToHandEffect copy() { + return new PulseOfTheFieldsReturnToHandEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId : controller.getInRange()) { + Player player = game.getPlayer(playerId); + if (player != null && player.getLife() > controller.getLife()) { + Card card = game.getCard(source.getSourceId()); + controller.moveCardToHandWithInfo(card, source.getSourceId(), game, Zone.STACK); + return true; + } + } + } + return false; + } +} From 3b3e06e91ae95ab00f17fdb59731a3b519c72093 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 1 Jun 2015 21:25:56 -0400 Subject: [PATCH 024/102] Implemented Artifact Blast, Clay Statue, Goblin Brigand, and Wall of Brambles. --- .../mage/sets/antiquities/ArtifactBlast.java | 67 ++++++++++++++++++ .../src/mage/sets/antiquities/ClayStatue.java | 38 ++++++++++ .../mage/sets/fifthedition/ClayStatue.java | 53 ++++++++++++++ .../sets/fifthedition/WallOfBrambles.java | 52 ++++++++++++++ .../mage/sets/fourthedition/ClayStatue.java | 53 ++++++++++++++ .../sets/fourthedition/WallOfBrambles.java | 52 ++++++++++++++ .../sets/limitedalpha/WallOfBrambles.java | 69 +++++++++++++++++++ .../mage/sets/limitedbeta/WallOfBrambles.java | 52 ++++++++++++++ .../sets/mastersedition/ArtifactBlast.java | 52 ++++++++++++++ .../sets/masterseditioniv/ArtifactBlast.java | 52 ++++++++++++++ .../sets/masterseditioniv/ClayStatue.java | 54 +++++++++++++++ .../mage/sets/ninthedition/GoblinBrigand.java | 63 +++++++++++++++++ .../sets/revisededition/WallOfBrambles.java | 52 ++++++++++++++ .../src/mage/sets/scourge/GoblinBrigand.java | 52 ++++++++++++++ .../sets/unlimitededition/WallOfBrambles.java | 52 ++++++++++++++ 15 files changed, 813 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/antiquities/ArtifactBlast.java create mode 100644 Mage.Sets/src/mage/sets/antiquities/ClayStatue.java create mode 100644 Mage.Sets/src/mage/sets/fifthedition/ClayStatue.java create mode 100644 Mage.Sets/src/mage/sets/fifthedition/WallOfBrambles.java create mode 100644 Mage.Sets/src/mage/sets/fourthedition/ClayStatue.java create mode 100644 Mage.Sets/src/mage/sets/fourthedition/WallOfBrambles.java create mode 100644 Mage.Sets/src/mage/sets/limitedalpha/WallOfBrambles.java create mode 100644 Mage.Sets/src/mage/sets/limitedbeta/WallOfBrambles.java create mode 100644 Mage.Sets/src/mage/sets/mastersedition/ArtifactBlast.java create mode 100644 Mage.Sets/src/mage/sets/masterseditioniv/ArtifactBlast.java create mode 100644 Mage.Sets/src/mage/sets/masterseditioniv/ClayStatue.java create mode 100644 Mage.Sets/src/mage/sets/ninthedition/GoblinBrigand.java create mode 100644 Mage.Sets/src/mage/sets/revisededition/WallOfBrambles.java create mode 100644 Mage.Sets/src/mage/sets/scourge/GoblinBrigand.java create mode 100644 Mage.Sets/src/mage/sets/unlimitededition/WallOfBrambles.java diff --git a/Mage.Sets/src/mage/sets/antiquities/ArtifactBlast.java b/Mage.Sets/src/mage/sets/antiquities/ArtifactBlast.java new file mode 100644 index 00000000000..a8f0dfe5f13 --- /dev/null +++ b/Mage.Sets/src/mage/sets/antiquities/ArtifactBlast.java @@ -0,0 +1,67 @@ +/* + * 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 mage.sets.antiquities; + +import java.util.UUID; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.target.TargetSpell; + +/** + * + * @author Jgod + */ +public class ArtifactBlast extends CardImpl { + private static final FilterSpell filter = new FilterSpell("artifact spell"); + + static { + filter.add(new CardTypePredicate(CardType.ARTIFACT)); + } + + public ArtifactBlast(UUID ownerId) { + super(ownerId, 87, "Artifact Blast", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}"); + this.expansionSetCode = "ATQ"; + + // Counter target artifact spell. + this.getSpellAbility().addTarget(new TargetSpell(filter)); + this.getSpellAbility().addEffect(new CounterTargetEffect()); + } + + public ArtifactBlast(final ArtifactBlast card) { + super(card); + } + + @Override + public ArtifactBlast copy() { + return new ArtifactBlast(this); + } +} diff --git a/Mage.Sets/src/mage/sets/antiquities/ClayStatue.java b/Mage.Sets/src/mage/sets/antiquities/ClayStatue.java new file mode 100644 index 00000000000..78636b4a4f3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/antiquities/ClayStatue.java @@ -0,0 +1,38 @@ +package mage.sets.antiquities; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class ClayStatue extends CardImpl { + + public ClayStatue(UUID ownerId) { + super(ownerId, 9, "Clay Statue", Rarity.COMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{4}"); + this.expansionSetCode = "ATQ"; + this.subtype.add("Golem"); + this.power = new MageInt(3); + this.toughness = new MageInt(1); + + // {2}: Regenerate Clay Statue. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{2}"))); + } + + public ClayStatue(final ClayStatue card) { + super(card); + } + + @Override + public ClayStatue copy() { + return new ClayStatue(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/fifthedition/ClayStatue.java b/Mage.Sets/src/mage/sets/fifthedition/ClayStatue.java new file mode 100644 index 00000000000..063a53eac8b --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/ClayStatue.java @@ -0,0 +1,53 @@ +/* + * 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 mage.sets.fifthedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class ClayStatue extends mage.sets.antiquities.ClayStatue { + + public ClayStatue(UUID ownerId) { + super(ownerId); + this.cardNumber = 355; + this.expansionSetCode = "5ED"; + } + + public ClayStatue(final ClayStatue card) { + super(card); + } + + @Override + public ClayStatue copy() { + return new ClayStatue(this); + } +} + diff --git a/Mage.Sets/src/mage/sets/fifthedition/WallOfBrambles.java b/Mage.Sets/src/mage/sets/fifthedition/WallOfBrambles.java new file mode 100644 index 00000000000..823d6e68494 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/WallOfBrambles.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.fifthedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends mage.sets.limitedalpha.WallOfBrambles { + + public WallOfBrambles(UUID ownerId) { + super(ownerId); + this.cardNumber = 200; + this.expansionSetCode = "5ED"; + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/ClayStatue.java b/Mage.Sets/src/mage/sets/fourthedition/ClayStatue.java new file mode 100644 index 00000000000..7924167129b --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/ClayStatue.java @@ -0,0 +1,53 @@ +/* + * 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 mage.sets.fourthedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class ClayStatue extends mage.sets.antiquities.ClayStatue { + + public ClayStatue(UUID ownerId) { + super(ownerId); + this.cardNumber = 323; + this.expansionSetCode = "4ED"; + } + + public ClayStatue(final ClayStatue card) { + super(card); + } + + @Override + public ClayStatue copy() { + return new ClayStatue(this); + } +} + diff --git a/Mage.Sets/src/mage/sets/fourthedition/WallOfBrambles.java b/Mage.Sets/src/mage/sets/fourthedition/WallOfBrambles.java new file mode 100644 index 00000000000..8e690dfa3d3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/WallOfBrambles.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.fourthedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends mage.sets.limitedalpha.WallOfBrambles { + + public WallOfBrambles(UUID ownerId) { + super(ownerId); + this.cardNumber = 166; + this.expansionSetCode = "4ED"; + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedalpha/WallOfBrambles.java b/Mage.Sets/src/mage/sets/limitedalpha/WallOfBrambles.java new file mode 100644 index 00000000000..68fa926717b --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedalpha/WallOfBrambles.java @@ -0,0 +1,69 @@ +/* + * 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 mage.sets.limitedalpha; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.abilities.keyword.DefenderAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends CardImpl { + + public WallOfBrambles(UUID ownerId) { + super(ownerId, 132, "Wall of Brambles", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{G}"); + this.expansionSetCode = "LEA"; + this.subtype.add("Plant"); + this.subtype.add("Wall"); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Defender + this.addAbility(DefenderAbility.getInstance()); + // {G}: Regenerate Wall of Brambles. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{G}"))); + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedbeta/WallOfBrambles.java b/Mage.Sets/src/mage/sets/limitedbeta/WallOfBrambles.java new file mode 100644 index 00000000000..0a677b85597 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedbeta/WallOfBrambles.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.limitedbeta; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends mage.sets.limitedalpha.WallOfBrambles { + + public WallOfBrambles(UUID ownerId) { + super(ownerId); + this.cardNumber = 132; + this.expansionSetCode = "LEB"; + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mastersedition/ArtifactBlast.java b/Mage.Sets/src/mage/sets/mastersedition/ArtifactBlast.java new file mode 100644 index 00000000000..9433ebb7cd0 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mastersedition/ArtifactBlast.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.mastersedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class ArtifactBlast extends mage.sets.antiquities.ArtifactBlast { + + public ArtifactBlast(UUID ownerId) { + super(ownerId); + this.cardNumber = 85; + this.expansionSetCode = "MED"; + } + + public ArtifactBlast(final ArtifactBlast card) { + super(card); + } + + @Override + public ArtifactBlast copy() { + return new ArtifactBlast(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/ArtifactBlast.java b/Mage.Sets/src/mage/sets/masterseditioniv/ArtifactBlast.java new file mode 100644 index 00000000000..ef3a5755c84 --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/ArtifactBlast.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.masterseditioniv; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class ArtifactBlast extends mage.sets.antiquities.ArtifactBlast { + + public ArtifactBlast(UUID ownerId) { + super(ownerId); + this.cardNumber = 108; + this.expansionSetCode = "ME4"; + } + + public ArtifactBlast(final ArtifactBlast card) { + super(card); + } + + @Override + public ArtifactBlast copy() { + return new ArtifactBlast(this); + } +} diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/ClayStatue.java b/Mage.Sets/src/mage/sets/masterseditioniv/ClayStatue.java new file mode 100644 index 00000000000..5e469e72ca6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/ClayStatue.java @@ -0,0 +1,54 @@ +/* + * 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 mage.sets.masterseditioniv; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class ClayStatue extends mage.sets.fourthedition.ClayStatue { + + public ClayStatue(UUID ownerId) { + super(ownerId); + this.cardNumber = 189; + this.expansionSetCode = "ME4"; + this.rarity = Rarity.UNCOMMON; + } + + public ClayStatue(final ClayStatue card) { + super(card); + } + + @Override + public ClayStatue copy() { + return new ClayStatue(this); + } +} diff --git a/Mage.Sets/src/mage/sets/ninthedition/GoblinBrigand.java b/Mage.Sets/src/mage/sets/ninthedition/GoblinBrigand.java new file mode 100644 index 00000000000..c25490133c9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ninthedition/GoblinBrigand.java @@ -0,0 +1,63 @@ +/* + * 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 mage.sets.ninthedition; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.common.AttacksEachTurnStaticAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class GoblinBrigand extends CardImpl { + + public GoblinBrigand(UUID ownerId) { + super(ownerId, 190, "Goblin Brigand", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); + this.expansionSetCode = "9ED"; + this.subtype.add("Goblin"); + this.subtype.add("Warrior"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Goblin Brigand attacks each turn if able. + this.addAbility(new AttacksEachTurnStaticAbility()); + } + + public GoblinBrigand(final GoblinBrigand card) { + super(card); + } + + @Override + public GoblinBrigand copy() { + return new GoblinBrigand(this); + } +} diff --git a/Mage.Sets/src/mage/sets/revisededition/WallOfBrambles.java b/Mage.Sets/src/mage/sets/revisededition/WallOfBrambles.java new file mode 100644 index 00000000000..ddc6bc8d1b7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/revisededition/WallOfBrambles.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.revisededition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends mage.sets.limitedalpha.WallOfBrambles { + + public WallOfBrambles(UUID ownerId) { + super(ownerId); + this.cardNumber = 131; + this.expansionSetCode = "3ED"; + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} diff --git a/Mage.Sets/src/mage/sets/scourge/GoblinBrigand.java b/Mage.Sets/src/mage/sets/scourge/GoblinBrigand.java new file mode 100644 index 00000000000..e82edc3224e --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/GoblinBrigand.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.scourge; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class GoblinBrigand extends mage.sets.ninthedition.GoblinBrigand { + + public GoblinBrigand(UUID ownerId) { + super(ownerId); + this.cardNumber = 94; + this.expansionSetCode = "SCG"; + } + + public GoblinBrigand(final GoblinBrigand card) { + super(card); + } + + @Override + public GoblinBrigand copy() { + return new GoblinBrigand(this); + } +} diff --git a/Mage.Sets/src/mage/sets/unlimitededition/WallOfBrambles.java b/Mage.Sets/src/mage/sets/unlimitededition/WallOfBrambles.java new file mode 100644 index 00000000000..a64740c9a1a --- /dev/null +++ b/Mage.Sets/src/mage/sets/unlimitededition/WallOfBrambles.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.unlimitededition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class WallOfBrambles extends mage.sets.limitedalpha.WallOfBrambles { + + public WallOfBrambles(UUID ownerId) { + super(ownerId); + this.cardNumber = 132; + this.expansionSetCode = "2ED"; + } + + public WallOfBrambles(final WallOfBrambles card) { + super(card); + } + + @Override + public WallOfBrambles copy() { + return new WallOfBrambles(this); + } +} From a97913bd4624d02668352809f6d8938f07023cce Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 1 Jun 2015 21:47:21 -0400 Subject: [PATCH 025/102] Added Mythic Proportions. --- .../sets/onslaught/MythicProportions.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/onslaught/MythicProportions.java diff --git a/Mage.Sets/src/mage/sets/onslaught/MythicProportions.java b/Mage.Sets/src/mage/sets/onslaught/MythicProportions.java new file mode 100644 index 00000000000..d5e6dc8b23b --- /dev/null +++ b/Mage.Sets/src/mage/sets/onslaught/MythicProportions.java @@ -0,0 +1,82 @@ +/* + * 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 mage.sets.onslaught; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Jgod + */ +public class MythicProportions extends CardImpl { + + public MythicProportions(UUID ownerId) { + super(ownerId, 274, "Mythic Proportions", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}{G}{G}"); + this.expansionSetCode = "ONS"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + + // Enchanted creature gets +8/+8 and has trample. + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEnchantedEffect(8, 8)); + Effect effect = new GainAbilityAttachedEffect(TrampleAbility.getInstance(), AttachmentType.AURA); + effect.setText("and has trample"); + ability.addEffect(effect); + this.addAbility(ability); + } + + public MythicProportions(final MythicProportions card) { + super(card); + } + + @Override + public MythicProportions copy() { + return new MythicProportions(this); + } +} From 5d706c909f64e64f74963b47248de362cc5eadb7 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 1 Jun 2015 21:59:15 -0400 Subject: [PATCH 026/102] Added Heart of Ramos. --- .../sets/mercadianmasques/HeartOfRamos.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/mercadianmasques/HeartOfRamos.java diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/HeartOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/HeartOfRamos.java new file mode 100644 index 00000000000..d87eaaef7b8 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/HeartOfRamos.java @@ -0,0 +1,65 @@ +/* + * 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 mage.sets.mercadianmasques; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.mana.RedManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class HeartOfRamos extends CardImpl { + + public HeartOfRamos(UUID ownerId) { + super(ownerId, 296, "Heart of Ramos", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MMQ"; + + // {tap}: Add {R} to your mana pool. + this.addAbility(new RedManaAbility()); + + // Sacrifice Heart of Ramos: Add {R} to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.RedMana, new SacrificeSourceCost())); + } + + public HeartOfRamos(final HeartOfRamos card) { + super(card); + } + + @Override + public HeartOfRamos copy() { + return new HeartOfRamos(this); + } +} From 3c3bd8da210cd62494ef0eb0499d0c45c1ccd1d8 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 1 Jun 2015 22:02:27 -0400 Subject: [PATCH 027/102] Added Skull of Ramos. --- .../sets/mercadianmasques/SkullOfRamos.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/mercadianmasques/SkullOfRamos.java diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/SkullOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/SkullOfRamos.java new file mode 100644 index 00000000000..0e9b94566b2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/SkullOfRamos.java @@ -0,0 +1,65 @@ +/* + * 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 mage.sets.mercadianmasques; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.mana.BlackManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class SkullOfRamos extends CardImpl { + + public SkullOfRamos(UUID ownerId) { + super(ownerId, 312, "Skull of Ramos", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MMQ"; + + // {tap}: Add {B} to your mana pool. + this.addAbility(new BlackManaAbility()); + + // Sacrifice Heart of Ramos: Add {B} to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlackMana, new SacrificeSourceCost())); + } + + public SkullOfRamos(final SkullOfRamos card) { + super(card); + } + + @Override + public SkullOfRamos copy() { + return new SkullOfRamos(this); + } +} From 69d635bbc6b413970f3df3c2e67f826e20a4842d Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 1 Jun 2015 22:04:11 -0400 Subject: [PATCH 028/102] Added Tooth of Ramos. --- .../sets/mercadianmasques/ToothOfRamos.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/mercadianmasques/ToothOfRamos.java diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/ToothOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/ToothOfRamos.java new file mode 100644 index 00000000000..df5ed3aa9a3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/ToothOfRamos.java @@ -0,0 +1,65 @@ +/* + * 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 mage.sets.mercadianmasques; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.mana.SimpleManaAbility; +import mage.abilities.mana.WhiteManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class ToothOfRamos extends CardImpl { + + public ToothOfRamos(UUID ownerId) { + super(ownerId, 313, "Tooth of Ramos", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MMQ"; + + // {tap}: Add {W} to your mana pool. + this.addAbility(new WhiteManaAbility()); + + // Sacrifice Heart of Ramos: Add {R} to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.WhiteMana, new SacrificeSourceCost())); + } + + public ToothOfRamos(final ToothOfRamos card) { + super(card); + } + + @Override + public ToothOfRamos copy() { + return new ToothOfRamos(this); + } +} From 17d3555bc505b78ebd7351798326f2e1ff280837 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 1 Jun 2015 22:06:37 -0400 Subject: [PATCH 029/102] Added Horn of Ramos and fixed other Ramos artifacts' comments (didn't feel like seperate commit). --- .../sets/mercadianmasques/HornOfRamos.java | 65 +++++++++++++++++++ .../sets/mercadianmasques/SkullOfRamos.java | 2 +- .../sets/mercadianmasques/ToothOfRamos.java | 2 +- 3 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/mercadianmasques/HornOfRamos.java diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/HornOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/HornOfRamos.java new file mode 100644 index 00000000000..c7c3072a846 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/HornOfRamos.java @@ -0,0 +1,65 @@ +/* + * 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 mage.sets.mercadianmasques; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.mana.GreenManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class HornOfRamos extends CardImpl { + + public HornOfRamos(UUID ownerId) { + super(ownerId, 299, "Horn of Ramos", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MMQ"; + + // {tap}: Add {G} to your mana pool. + this.addAbility(new GreenManaAbility()); + + // Sacrifice Horn of Ramos: Add {G} to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.GreenMana, new SacrificeSourceCost())); + } + + public HornOfRamos(final HornOfRamos card) { + super(card); + } + + @Override + public HornOfRamos copy() { + return new HornOfRamos(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/SkullOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/SkullOfRamos.java index 0e9b94566b2..ca888a25691 100644 --- a/Mage.Sets/src/mage/sets/mercadianmasques/SkullOfRamos.java +++ b/Mage.Sets/src/mage/sets/mercadianmasques/SkullOfRamos.java @@ -50,7 +50,7 @@ public class SkullOfRamos extends CardImpl { // {tap}: Add {B} to your mana pool. this.addAbility(new BlackManaAbility()); - // Sacrifice Heart of Ramos: Add {B} to your mana pool. + // Sacrifice Skull of Ramos: Add {B} to your mana pool. this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlackMana, new SacrificeSourceCost())); } diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/ToothOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/ToothOfRamos.java index df5ed3aa9a3..6017ba27e36 100644 --- a/Mage.Sets/src/mage/sets/mercadianmasques/ToothOfRamos.java +++ b/Mage.Sets/src/mage/sets/mercadianmasques/ToothOfRamos.java @@ -50,7 +50,7 @@ public class ToothOfRamos extends CardImpl { // {tap}: Add {W} to your mana pool. this.addAbility(new WhiteManaAbility()); - // Sacrifice Heart of Ramos: Add {R} to your mana pool. + // Sacrifice Tooth of Ramos: Add {W} to your mana pool. this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.WhiteMana, new SacrificeSourceCost())); } From df5ed7e2b12b14568dcd9a72a074fa3c67d090a4 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 1 Jun 2015 22:09:45 -0400 Subject: [PATCH 030/102] Added Eye of Ramos. --- .../sets/mercadianmasques/EyeOfRamos.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/mercadianmasques/EyeOfRamos.java diff --git a/Mage.Sets/src/mage/sets/mercadianmasques/EyeOfRamos.java b/Mage.Sets/src/mage/sets/mercadianmasques/EyeOfRamos.java new file mode 100644 index 00000000000..91c629d22d1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mercadianmasques/EyeOfRamos.java @@ -0,0 +1,65 @@ +/* + * 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 mage.sets.mercadianmasques; + +import java.util.UUID; +import mage.Mana; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.mana.BlueManaAbility; +import mage.abilities.mana.SimpleManaAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class EyeOfRamos extends CardImpl { + + public EyeOfRamos(UUID ownerId) { + super(ownerId, 294, "Eye of Ramos", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MMQ"; + + // {tap}: Add {U} to your mana pool. + this.addAbility(new BlueManaAbility()); + + // Sacrifice Eye of Ramos: Add {U} to your mana pool. + this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.BlueMana, new SacrificeSourceCost())); + } + + public EyeOfRamos(final EyeOfRamos card) { + super(card); + } + + @Override + public EyeOfRamos copy() { + return new EyeOfRamos(this); + } +} From ff98d58756b61af094ae438d8bd5bc008237228d Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 1 Jun 2015 22:33:04 -0400 Subject: [PATCH 031/102] Added Fissure. --- .../src/mage/sets/fourthedition/Fissure.java | 52 ++++++++++++++ .../src/mage/sets/mastersedition/Fissure.java | 52 ++++++++++++++ Mage.Sets/src/mage/sets/thedark/Fissure.java | 70 +++++++++++++++++++ 3 files changed, 174 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/fourthedition/Fissure.java create mode 100644 Mage.Sets/src/mage/sets/mastersedition/Fissure.java create mode 100644 Mage.Sets/src/mage/sets/thedark/Fissure.java diff --git a/Mage.Sets/src/mage/sets/fourthedition/Fissure.java b/Mage.Sets/src/mage/sets/fourthedition/Fissure.java new file mode 100644 index 00000000000..6c0e119468f --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/Fissure.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.fourthedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class Fissure extends mage.sets.thedark.Fissure { + + public Fissure(UUID ownerId) { + super(ownerId); + this.cardNumber = 212; + this.expansionSetCode = "4ED"; + } + + public Fissure(final Fissure card) { + super(card); + } + + @Override + public Fissure copy() { + return new Fissure(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mastersedition/Fissure.java b/Mage.Sets/src/mage/sets/mastersedition/Fissure.java new file mode 100644 index 00000000000..1999cce4486 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mastersedition/Fissure.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.mastersedition; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class Fissure extends mage.sets.thedark.Fissure { + + public Fissure(UUID ownerId) { + super(ownerId); + this.cardNumber = 93; + this.expansionSetCode = "MED"; + } + + public Fissure(final Fissure card) { + super(card); + } + + @Override + public Fissure copy() { + return new Fissure(this); + } +} diff --git a/Mage.Sets/src/mage/sets/thedark/Fissure.java b/Mage.Sets/src/mage/sets/thedark/Fissure.java new file mode 100644 index 00000000000..63f5d958355 --- /dev/null +++ b/Mage.Sets/src/mage/sets/thedark/Fissure.java @@ -0,0 +1,70 @@ +/* + * 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 mage.sets.thedark; + +import java.util.UUID; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.filter.FilterPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.target.Target; +import mage.target.TargetPermanent; + +/** + * + * @author Jgod + */ +public class Fissure extends CardImpl { + private static final FilterPermanent filter = new FilterPermanent("creature or land"); + static { + filter.add(Predicates.or(new CardTypePredicate(CardType.CREATURE), new CardTypePredicate(CardType.LAND))); + } + + public Fissure(UUID ownerId) { + super(ownerId, 62, "Fissure", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{3}{R}{R}"); + this.expansionSetCode = "DRK"; + + // Destroy target creature or land. It can't be regenerated. + this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); + Target target = new TargetPermanent(filter); + this.getSpellAbility().addTarget(target); + } + + public Fissure(final Fissure card) { + super(card); + } + + @Override + public Fissure copy() { + return new Fissure(this); + } +} + From 3115bbdcb6773bb49a05ac42f8e765ef7c1bc369 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 2 Jun 2015 14:58:54 +0200 Subject: [PATCH 032/102] Multiplayer test - added option to attack specific player. --- .../java/org/mage/test/player/TestPlayer.java | 26 ++++++++++++++----- .../base/impl/CardTestPlayerAPIImpl.java | 4 +++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index ecca9bfc4a9..60bc095e147 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -135,7 +135,7 @@ public class TestPlayer extends ComputerPlayer { if (ability.toString().startsWith(groups[0])) { Ability newAbility = ability.copy(); if (groups.length > 1 && !groups[1].equals("target=NO_TARGET")) { - if (!addTargets(newAbility, groups, game)) { + if (!addTargets(newAbility, groups, game)) { // targets could not be set -> try next priority break; } @@ -198,12 +198,7 @@ public class TestPlayer extends ComputerPlayer { UUID defenderId = null; for (PlayerAction action: actions) { if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) { - for (UUID uuid: game.getCombat().getDefenders()) { - Player defender = game.getPlayer(uuid); - if (defender != null) { - defenderId = uuid; - } - } + String command = action.getAction(); command = command.substring(command.indexOf("attack:") + 7); String[] groups = command.split("\\$"); @@ -217,7 +212,24 @@ public class TestPlayer extends ComputerPlayer { } } } + if (group.startsWith("defendingPlayer=")) { + String defendingPlayerName = group.substring(group.indexOf("defendingPlayer=") + 16); + for (Player defendingPlayer :game.getPlayers().values()) { + if (defendingPlayer.getName().equals(defendingPlayerName)) { + defenderId = defendingPlayer.getId(); + break; + } + } + } } + if (defenderId == null) { + for (UUID uuid: game.getCombat().getDefenders()) { + Player defender = game.getPlayer(uuid); + if (defender != null) { + defenderId = uuid; + } + } + } FilterCreatureForCombat filter = new FilterCreatureForCombat(); filter.add(new NamePredicate(groups[0])); filter.add(Predicates.not(new AttackingPredicate())); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index abb23bd72ad..b1eb8560ff8 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -954,6 +954,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public void attack(int turnNum, TestPlayer player, String attacker) { player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:"+attacker); } + + public void attack(int turnNum, TestPlayer player, String attacker, TestPlayer defendingPlayer) { + player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, "attack:"+attacker+"$defendingPlayer="+defendingPlayer.getName()); + } public void attack(int turnNum, TestPlayer player, String attacker, String planeswalker) { player.addAction(turnNum, PhaseStep.DECLARE_ATTACKERS, new StringBuilder("attack:").append(attacker).append("$planeswalker=").append(planeswalker).toString()); From f9f674b8f7b663fc776832e14400e98872fa00fc Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 2 Jun 2015 15:01:52 +0200 Subject: [PATCH 033/102] * Fixed that removed players (e.g. by lethal damage) changed the players in range wrongly immediatly instead of at the start of the next turn. --- .../sets/zendikar/BloodchiefAscension.java | 3 +- .../multiplayer/BloodchiefAscensionTest.java | 146 ++++++++++++++++++ Mage/src/mage/game/GameImpl.java | 9 +- 3 files changed, 150 insertions(+), 8 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/multiplayer/BloodchiefAscensionTest.java diff --git a/Mage.Sets/src/mage/sets/zendikar/BloodchiefAscension.java b/Mage.Sets/src/mage/sets/zendikar/BloodchiefAscension.java index d57c523f802..de0290a429d 100644 --- a/Mage.Sets/src/mage/sets/zendikar/BloodchiefAscension.java +++ b/Mage.Sets/src/mage/sets/zendikar/BloodchiefAscension.java @@ -57,10 +57,9 @@ public class BloodchiefAscension extends CardImpl { super(ownerId, 82, "Bloodchief Ascension", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{B}"); this.expansionSetCode = "ZEN"; - // At the beginning of each end step, if an opponent lost 2 or more life this turn, you may put a quest counter on Bloodchief Ascension. (Damage causes loss of life.) this.addAbility(new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, - new AddCountersSourceEffect(CounterType.QUEST.createInstance(), false), + new AddCountersSourceEffect(CounterType.QUEST.createInstance(1), false), TargetController.ANY, new OpponentLostLifeCondition(Condition.ComparisonType.GreaterThan, 1), true)); diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/BloodchiefAscensionTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/BloodchiefAscensionTest.java new file mode 100644 index 00000000000..abad3873732 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/BloodchiefAscensionTest.java @@ -0,0 +1,146 @@ +/* + * 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.multiplayer; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestMultiPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BloodchiefAscensionTest extends CardTestMultiPlayerBase { + + @Test + public void testBloodchiefAscensionAllPlayers() { + // Enchantment + // At the beginning of each end step, if an opponent lost 2 or more life this turn, you may put a quest counter on Bloodchief Ascension. (Damage causes loss of life.) + // Whenever a card is put into an opponent's graveyard from anywhere, if Bloodchief Ascension has three or more quest counters on it, you may have that player lose 2 life. If you do, you gain 2 life. + addCard(Zone.BATTLEFIELD, playerA, "Bloodchief Ascension"); + + addCard(Zone.BATTLEFIELD, playerA, "Mountain",3); + addCard(Zone.HAND, playerA, "Fireball"); + addCard(Zone.BATTLEFIELD, playerD, "Mountain",3); + addCard(Zone.HAND, playerD, "Fireball"); + addCard(Zone.BATTLEFIELD, playerC, "Mountain",3); + addCard(Zone.HAND, playerC, "Fireball"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain",3); + addCard(Zone.HAND, playerB, "Fireball"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fireball", playerA); + setChoice(playerA, "X=2"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Fireball", playerD); + setChoice(playerD, "X=2"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Fireball", playerC); + setChoice(playerC, "X=2"); + + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Fireball", playerB); + setChoice(playerB, "X=2"); + + // Player order: A -> D -> C -> B + + setStopAt(4, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 18); + assertLife(playerB, 18); + assertLife(playerC, 18); + assertLife(playerD, 18); + + assertGraveyardCount(playerA, "Fireball", 1); + assertGraveyardCount(playerB, "Fireball", 1); + assertGraveyardCount(playerC, "Fireball", 1); + assertGraveyardCount(playerD, "Fireball", 1); + + + + assertCounterCount("Bloodchief Ascension", CounterType.QUEST, 2); // 1 opponent out of range + + } + /** + * One of my opponents in a multiplayer game had a Bloodchief Ascension in play. I took lethal damage on my turn, + * but he didn't get a counter on Bloodchief Ascension at my end step. I think he should, even though I had left + * the game from dying, because of: + * + * 800.4g. If a player leaves the game during his or her turn, that turn continues to its completion without an + * active player. If the active player would receive priority, instead the next player in turn order receives priority, + * or the top object on the stack resolves, or the phase or step ends, whichever is appropriate. + */ + @Test + public void testBloodchiefAscension() { + // Enchantment + // At the beginning of each end step, if an opponent lost 2 or more life this turn, you may put a quest counter on Bloodchief Ascension. (Damage causes loss of life.) + // Whenever a card is put into an opponent's graveyard from anywhere, if Bloodchief Ascension has three or more quest counters on it, you may have that player lose 2 life. If you do, you gain 2 life. + addCard(Zone.BATTLEFIELD, playerA, "Bloodchief Ascension"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain",3); + addCard(Zone.HAND, playerA, "Fireball"); + addCard(Zone.BATTLEFIELD, playerD, "Mountain",3); + addCard(Zone.HAND, playerD, "Fireball"); + addCard(Zone.BATTLEFIELD, playerC, "Mountain",3); + addCard(Zone.HAND, playerC, "Fireball"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain",21); + addCard(Zone.HAND, playerB, "Fireball"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fireball", playerA); + setChoice(playerA, "X=2"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerD, "Fireball", playerD); + setChoice(playerD, "X=2"); + + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerC, "Fireball", playerC); + setChoice(playerC, "X=2"); + + castSpell(4, PhaseStep.PRECOMBAT_MAIN, playerB, "Fireball", playerB); + setChoice(playerB, "X=20"); + + // Player order: A -> D -> C -> B + + setStopAt(4, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 18); + assertLife(playerB, 0); + assertLife(playerC, 18); + assertLife(playerD, 18); + + Assert.assertTrue("playerB has lost", playerB.hasLost()); + + assertGraveyardCount(playerA, "Fireball", 1); + assertGraveyardCount(playerC, "Fireball", 1); + assertGraveyardCount(playerD, "Fireball", 1); + + assertCounterCount("Bloodchief Ascension", CounterType.QUEST, 2); // 1 opponent out of range + + } +} diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 8251d6e7adc..0de2767ce8b 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -2100,12 +2100,9 @@ public abstract class GameImpl implements Game, Serializable { } } - // Update players in range of - for (Player leftPlayer :this.getPlayers().values()) { - if (leftPlayer.isInGame()) { - leftPlayer.otherPlayerLeftGame(this); - } - } + // 801.2c The particular players within each player‘s range of influence are determined as each turn begins. + // So no update of range if influence yet + } @Override From 78961ad511ac40123df1432741976bcf666d2455 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 2 Jun 2015 17:08:49 +0200 Subject: [PATCH 034/102] Added Vesuva test. --- .../sets/scarsofmirrodin/Glimmerpost.java | 6 +- .../org/mage/test/cards/copy/VesuvaTest.java | 105 ++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/copy/VesuvaTest.java diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Glimmerpost.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Glimmerpost.java index 3fdb0f9a509..39eb17192cd 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Glimmerpost.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Glimmerpost.java @@ -53,8 +53,12 @@ public class Glimmerpost extends CardImpl { super(ownerId, 227, "Glimmerpost", Rarity.COMMON, new CardType[]{CardType.LAND}, null); this.expansionSetCode = "SOM"; this.subtype.add("Locus"); - this.addAbility(new ColorlessManaAbility()); + + // When Glimmerpost enters the battlefield, you gain 1 life for each Locus on the battlefield. this.addAbility(new EntersBattlefieldTriggeredAbility(new GlimmerpostEffect())); + + // {T}: Add to {1} your mana pool. + this.addAbility(new ColorlessManaAbility()); } public Glimmerpost (final Glimmerpost card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/VesuvaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VesuvaTest.java new file mode 100644 index 00000000000..601be2d21f4 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/VesuvaTest.java @@ -0,0 +1,105 @@ +/* + * 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.cards.copy; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class VesuvaTest extends CardTestPlayerBase { + + /** + * played a Vesuva on Glimmerpost and since when Vesuva becames Glimmerpost + * it is already on the battlefield the ability won't trigger. Then when i + * used Vesuva to copy an opponent's Dark Depth i got the land with the 10 + * counters. + * + * If Dark Depth says "it enters the battlefield with 10 counter" but Vesuva + * is already on the field shouldnt have any counters or the problem was + * that Glimmerpost should have trigger? + */ + @Test + public void testGlimmerpost() { + // When Glimmerpost enters the battlefield, you gain 1 life for each Locus on the battlefield. + // {T}: {1} Add to your mana pool. + addCard(Zone.HAND, playerA, "Glimmerpost", 1); + // You may have Vesuva enter the battlefield tapped as a copy of any land on the battlefield. + addCard(Zone.HAND, playerA, "Vesuva", 1); + + addCard(Zone.HAND, playerB, "Glimmerpost", 1); + + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Glimmerpost"); + playLand(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Glimmerpost"); + playLand(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Vesuva"); + setChoice(playerA, "Glimmerpost"); + + setStopAt(3, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Glimmerpost", 2); + assertPermanentCount(playerB, "Glimmerpost", 1); + + assertLife(playerA, 24); // 20 + 1 + 3 + assertLife(playerB, 22); // 20 + 2 + } + + @Test + public void testDarkDepth() { + // Dark Depths enters the battlefield with ten ice counters on it. + // {3}: Remove an ice counter from Dark Depths. + // When Dark Depths has no ice counters on it, sacrifice it. If you do, put a legendary 20/20 black Avatar creature token with flying and "This creature is indestructible" named Marit Lage onto the battlefield. + addCard(Zone.BATTLEFIELD, playerB, "Dark Depths", 1); + + // You may have Vesuva enter the battlefield tapped as a copy of any land on the battlefield. + addCard(Zone.HAND, playerA, "Vesuva", 1); + + playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vesuva"); + setChoice(playerA, "Dark Depths"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Dark Depths", 1); + assertPermanentCount(playerB, "Dark Depths", 1); + + Permanent darkDepth = getPermanent("Dark Depths", playerA); + if (darkDepth != null) { + Assert.assertEquals(darkDepth.getCounters().getCount("ice"), 10); + } + + } + +} From bb28394f715dfffc1776e542d52e7b2742ab2f61 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 2 Jun 2015 22:50:57 +0200 Subject: [PATCH 035/102] * Helm of Obedience - Fixed that card movement to graveyard was not handled correct. It was not checked, if card was really moved to graveyard (fixes #1004). --- .../mage/sets/alliances/HelmOfObedience.java | 40 +++++----- .../replacement/LeylineOfTheVoidTest.java | 76 +++++++++++++++++++ Mage/src/mage/players/PlayerImpl.java | 15 ++-- .../common/PlayerLostLifeWatcher.java | 8 +- 4 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java diff --git a/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java b/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java index 6f3768f2913..76325f76e42 100644 --- a/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java +++ b/Mage.Sets/src/mage/sets/alliances/HelmOfObedience.java @@ -97,32 +97,32 @@ class HelmOfObedienceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null) { + Player targetOpponent = game.getPlayer(targetPointer.getFirst(game, source)); + if (targetOpponent != null) { int max = amount.calculate(game, source, this); if(max != 0){ int numberOfCard = 0; - while(player.getLibrary().size() > 0) { - Card card = player.getLibrary().removeFromTop(game); + while(targetOpponent.getLibrary().size() > 0) { + Card card = targetOpponent.getLibrary().removeFromTop(game); if (card != null){ - player.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game); - if(card.getCardType().contains(CardType.CREATURE)){ - // If a creature card is put into that graveyard this way, sacrifice Helm of Obedience - // and put that card onto the battlefield under your control. - Permanent sourcePermanent = game.getPermanent(source.getSourceId()); - if (sourcePermanent != null) { - sourcePermanent.sacrifice(source.getSourceId(), game); - } - if (game.getState().getZone(card.getId()).equals(Zone.GRAVEYARD)) { - card.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId()); - } - break; - } - else{ - numberOfCard++; - if(numberOfCard >= max){ + if (targetOpponent.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game)) { + if(card.getCardType().contains(CardType.CREATURE)){ + // If a creature card is put into that graveyard this way, sacrifice Helm of Obedience + // and put that card onto the battlefield under your control. + Permanent sourcePermanent = game.getPermanent(source.getSourceId()); + if (sourcePermanent != null) { + sourcePermanent.sacrifice(source.getSourceId(), game); + } + if (game.getState().getZone(card.getId()).equals(Zone.GRAVEYARD)) { + card.putOntoBattlefield(game, Zone.GRAVEYARD, source.getSourceId(), source.getControllerId()); + } break; + } else{ + numberOfCard++; + if(numberOfCard >= max){ + break; + } } } } else{ diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java new file mode 100644 index 00000000000..20b7bdec98d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/LeylineOfTheVoidTest.java @@ -0,0 +1,76 @@ +/* + * 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.cards.replacement; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class LeylineOfTheVoidTest extends CardTestPlayerBase { + + /** + * Leyline of the Void is on the battlefield, Helm of Obedience is used on + * an opponent with X=1. Now the Helm states to mill "until a creature card + * or X cards are put into the graveyard this way". For each of those milled + * cards, Leyline states to "remove [them] from the game instead". In other + * words: Leyline + Helm and X of at least 1 exiles the library of one + * unlucky opponent. Or should, because right now it doesn't. Right now it's + * Helm's originally intended "X or 1st Creature" amount of cards that after + * it's effect get exiled. + */ + @Test + public void testGrindstoneProgenius() { + addCard(Zone.BATTLEFIELD, playerA, "Plains"); + + // If Leyline of the Void is in your opening hand, you may begin the game with it on the battlefield. + // If a card would be put into an opponent's graveyard from anywhere, exile it instead. + addCard(Zone.BATTLEFIELD, playerA, "Leyline of the Void"); + + // {X}, {T}: Target opponent puts cards from the top of his or her library into his or her graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice Helm of Obedience and put that card onto the battlefield under your control. X can't be 0. + addCard(Zone.BATTLEFIELD, playerA, "Helm of Obedience"); + + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{X},{T}: Target opponent puts cards", playerB); + setChoice(playerA, "X=1"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertExileCount(playerB, 71); // All cards go to exile replaced from Leyline of the void + } + +} + + diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 16ce833b928..ccb4b5fa2a2 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2900,6 +2900,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean moveCardsToGraveyardWithInfo(List allCards, Ability source, Game game, Zone fromZone) { + boolean result = true; + UUID sourceId = source == null ? null : source.getSourceId(); while (!allCards.isEmpty()) { // identify cards from one owner Cards cards = new CardsImpl(); @@ -2914,7 +2916,7 @@ public abstract class PlayerImpl implements Player, Serializable { cards.add(card); } } - // move cards ot graveyard in order the owner decides + // move cards to graveyard in order the owner decides if (!cards.isEmpty()) { Player choosingPlayer = this; if (ownerId != this.getId()) { @@ -2936,22 +2938,21 @@ public abstract class PlayerImpl implements Player, Serializable { Card card = cards.get(targetObjectId, game); cards.remove(targetObjectId); if (card != null) { - choosingPlayer.moveCardToGraveyardWithInfo(card, source.getSourceId(), game, fromZone); + result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone); } target.clearChosen(); } if (cards.size() == 1) { - choosingPlayer.moveCardToGraveyardWithInfo(cards.getCards(game).iterator().next(), source == null ? null : source.getSourceId(), game, fromZone); + result &= choosingPlayer.moveCardToGraveyardWithInfo(cards.getCards(game).iterator().next(), sourceId, game, fromZone); } } else { for (Card card : cards.getCards(game)) { - choosingPlayer.moveCardToGraveyardWithInfo(card, source.getSourceId(), game, fromZone); + result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone); } } - } - + } } - return true; + return result; } @Override diff --git a/Mage/src/mage/watchers/common/PlayerLostLifeWatcher.java b/Mage/src/mage/watchers/common/PlayerLostLifeWatcher.java index ae1a8247fe5..26b16292c40 100644 --- a/Mage/src/mage/watchers/common/PlayerLostLifeWatcher.java +++ b/Mage/src/mage/watchers/common/PlayerLostLifeWatcher.java @@ -68,9 +68,9 @@ public class PlayerLostLifeWatcher extends Watcher { if (playerId != null) { Integer amount = amountOfLifeLostThisTurn.get(playerId); if (amount == null) { - amount = Integer.valueOf(event.getAmount()); + amount = event.getAmount(); } else { - amount = Integer.valueOf(amount + event.getAmount()); + amount = amount + event.getAmount(); } amountOfLifeLostThisTurn.put(playerId, amount); } @@ -80,7 +80,7 @@ public class PlayerLostLifeWatcher extends Watcher { public int getLiveLost(UUID playerId) { Integer amount = amountOfLifeLostThisTurn.get(playerId); if (amount != null) { - return amount.intValue(); + return amount; } return 0; } @@ -88,7 +88,7 @@ public class PlayerLostLifeWatcher extends Watcher { public int getLiveLostLastTurn(UUID playerId) { Integer amount = amountOfLifeLostLastTurn.get(playerId); if (amount != null) { - return amount.intValue(); + return amount; } return 0; } From cafb8c7c0c43c84bac64874572250a6904d79324 Mon Sep 17 00:00:00 2001 From: Zeplar Date: Tue, 2 Jun 2015 14:12:51 -0700 Subject: [PATCH 036/102] Added the W/U/B/R/G Scarab auras from Ice Age. Fixed minor typo in Can't Be Blocked By <> text. --- .../src/mage/sets/iceage/BlackScarab.java | 113 +++++++++++++++++ .../src/mage/sets/iceage/BlueScarab.java | 113 +++++++++++++++++ .../src/mage/sets/iceage/GreenScarab.java | 113 +++++++++++++++++ Mage.Sets/src/mage/sets/iceage/RedScarab.java | 113 +++++++++++++++++ .../src/mage/sets/iceage/WhiteScarab.java | 114 ++++++++++++++++++ .../CanBlockOnlyFlyingAttachedEffect.java | 2 +- .../combat/CantBeBlockedAttachedEffect.java | 2 +- ...antBeBlockedByCreaturesAttachedEffect.java | 4 +- 8 files changed, 570 insertions(+), 4 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/iceage/BlackScarab.java create mode 100644 Mage.Sets/src/mage/sets/iceage/BlueScarab.java create mode 100644 Mage.Sets/src/mage/sets/iceage/GreenScarab.java create mode 100644 Mage.Sets/src/mage/sets/iceage/RedScarab.java create mode 100644 Mage.Sets/src/mage/sets/iceage/WhiteScarab.java diff --git a/Mage.Sets/src/mage/sets/iceage/BlackScarab.java b/Mage.Sets/src/mage/sets/iceage/BlackScarab.java new file mode 100644 index 00000000000..3ced830ddc5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/BlackScarab.java @@ -0,0 +1,113 @@ +/* + * 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 mage.sets.iceage; + +import java.util.Set; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class BlackScarab extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("black creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLACK)); + } + + public BlackScarab(UUID ownerId) { + super(ownerId, 230, "Black Scarab", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + this.expansionSetCode = "ICE"; + this.subtype.add("Aura"); + +// Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature can't be blocked by black creatures. + Effect effect = new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, filter, AttachmentType.AURA); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 as long as an opponent controls a black permanent. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield), + new BlackScarabCondition(), "Enchanted creature gets +2/+2 as long as an opponent controls a black permanent"))); + } + + public BlackScarab(final BlackScarab card) { + super(card); + } + + @Override + public BlackScarab copy() { + return new BlackScarab(this); + } +} + +class BlackScarabCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + boolean conditionApplies = false; + FilterPermanent filter = new FilterPermanent(); + filter.add(new ColorPredicate(ObjectColor.BLACK)); + Set opponents = game.getOpponents(source.getControllerId()); + for (UUID opponentId : opponents) { + conditionApplies |= game.getBattlefield().countAll(filter, opponentId, game) > 0; + } + return conditionApplies; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/iceage/BlueScarab.java b/Mage.Sets/src/mage/sets/iceage/BlueScarab.java new file mode 100644 index 00000000000..f2d82c989c2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/BlueScarab.java @@ -0,0 +1,113 @@ +/* + * 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 mage.sets.iceage; + +import java.util.Set; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class BlueScarab extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("blue creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLUE)); + } + + public BlueScarab(UUID ownerId) { + super(ownerId, 233, "Blue Scarab", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + this.expansionSetCode = "ICE"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature can't be blocked by blue creatures. + Effect effect = new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, filter, AttachmentType.AURA); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 as long as an opponent controls a blue permanent. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield), + new BlueScarabCondition(), "Enchanted creature gets +2/+2 as long as an opponent controls a blue permanent"))); + } + + public BlueScarab(final BlueScarab card) { + super(card); + } + + @Override + public BlueScarab copy() { + return new BlueScarab(this); + } +} + +class BlueScarabCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + boolean conditionApplies = false; + FilterPermanent filter = new FilterPermanent(); + filter.add(new ColorPredicate(ObjectColor.BLUE)); + Set opponents = game.getOpponents(source.getControllerId()); + for (UUID opponentId : opponents) { + conditionApplies |= game.getBattlefield().countAll(filter, opponentId, game) > 0; + } + return conditionApplies; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/iceage/GreenScarab.java b/Mage.Sets/src/mage/sets/iceage/GreenScarab.java new file mode 100644 index 00000000000..d3d846e2ad5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/GreenScarab.java @@ -0,0 +1,113 @@ +/* + * 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 mage.sets.iceage; + +import java.util.Set; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class GreenScarab extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("green creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.GREEN)); + } + + public GreenScarab(UUID ownerId) { + super(ownerId, 252, "Green Scarab", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + this.expansionSetCode = "ICE"; + this.subtype.add("Aura"); + +// Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature can't be blocked by green creatures. + Effect effect = new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, filter, AttachmentType.AURA); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 as long as an opponent controls a white permanent. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield), + new GreenScarabCondition(), "Enchanted creature gets +2/+2 as long as an opponent controls a green permanent"))); + } + + public GreenScarab(final GreenScarab card) { + super(card); + } + + @Override + public GreenScarab copy() { + return new GreenScarab(this); + } +} + +class GreenScarabCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + boolean conditionApplies = false; + FilterPermanent filter = new FilterPermanent(); + filter.add(new ColorPredicate(ObjectColor.GREEN)); + Set opponents = game.getOpponents(source.getControllerId()); + for (UUID opponentId : opponents) { + conditionApplies |= game.getBattlefield().countAll(filter, opponentId, game) > 0; + } + return conditionApplies; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/iceage/RedScarab.java b/Mage.Sets/src/mage/sets/iceage/RedScarab.java new file mode 100644 index 00000000000..1cda24c5966 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/RedScarab.java @@ -0,0 +1,113 @@ +/* + * 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 mage.sets.iceage; + +import java.util.Set; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.Effect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class RedScarab extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("red creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.RED)); + } + + public RedScarab(UUID ownerId) { + super(ownerId, 273, "Red Scarab", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + this.expansionSetCode = "ICE"; + this.subtype.add("Aura"); + +// Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature can't be blocked by red creatures. + Effect effect = new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, filter, AttachmentType.AURA); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 as long as an opponent controls a red permanent. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield), + new RedScarabCondition(), "Enchanted creature gets +2/+2 as long as an opponent controls a red permanent"))); + } + + public RedScarab(final RedScarab card) { + super(card); + } + + @Override + public RedScarab copy() { + return new RedScarab(this); + } +} + +class RedScarabCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + boolean conditionApplies = false; + FilterPermanent filter = new FilterPermanent(); + filter.add(new ColorPredicate(ObjectColor.RED)); + Set opponents = game.getOpponents(source.getControllerId()); + for (UUID opponentId : opponents) { + conditionApplies |= game.getBattlefield().countAll(filter, opponentId, game) > 0; + } + return conditionApplies; + } +} diff --git a/Mage.Sets/src/mage/sets/iceage/WhiteScarab.java b/Mage.Sets/src/mage/sets/iceage/WhiteScarab.java new file mode 100644 index 00000000000..72429ccc8c1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/iceage/WhiteScarab.java @@ -0,0 +1,114 @@ +/* + * 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 mage.sets.iceage; + +import java.util.Set; +import java.util.UUID; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.decorator.ConditionalContinuousEffect; +import mage.abilities.effects.common.AttachEffect; +import mage.abilities.effects.common.continuous.BoostEnchantedEffect; +import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAttachedEffect; +import mage.abilities.effects.Effect; +import mage.abilities.keyword.EnchantAbility; +import mage.cards.CardImpl; +import mage.constants.AttachmentType; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.FilterPermanent; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.target.TargetPermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class WhiteScarab extends CardImpl { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("white creatures"); + + static { + filter.add(new ColorPredicate(ObjectColor.WHITE)); + } + + + public WhiteScarab(UUID ownerId) { + super(ownerId, 280, "White Scarab", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{W}"); + this.expansionSetCode = "ICE"; + this.subtype.add("Aura"); + + // Enchant creature + TargetPermanent auraTarget = new TargetCreaturePermanent(); + this.getSpellAbility().addTarget(auraTarget); + this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature)); + Ability ability = new EnchantAbility(auraTarget.getTargetName()); + this.addAbility(ability); + // Enchanted creature can't be blocked by white creatures. + Effect effect = new CantBeBlockedByCreaturesAttachedEffect(Duration.WhileOnBattlefield, filter, AttachmentType.AURA); + ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect); + this.addAbility(ability); + + // Enchanted creature gets +2/+2 as long as an opponent controls a white permanent. + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, + new ConditionalContinuousEffect(new BoostEnchantedEffect(2, 2, Duration.WhileOnBattlefield), + new WhiteScarabCondition(), "Enchanted creature gets +2/+2 as long as an opponent controls a white permanent"))); + } + + public WhiteScarab(final WhiteScarab card) { + super(card); + } + + @Override + public WhiteScarab copy() { + return new WhiteScarab(this); + } +} + +class WhiteScarabCondition implements Condition { + + @Override + public boolean apply(Game game, Ability source) { + boolean conditionApplies = false; + FilterPermanent filter = new FilterPermanent(); + filter.add(new ColorPredicate(ObjectColor.WHITE)); + Set opponents = game.getOpponents(source.getControllerId()); + for (UUID opponentId : opponents) { + conditionApplies |= game.getBattlefield().countAll(filter, opponentId, game) > 0; + } + return conditionApplies; + } +} diff --git a/Mage/src/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java b/Mage/src/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java index a457e94d455..ba0fba94acd 100644 --- a/Mage/src/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/combat/CanBlockOnlyFlyingAttachedEffect.java @@ -48,7 +48,7 @@ public class CanBlockOnlyFlyingAttachedEffect extends RestrictionEffect { if (attachmentType.equals(AttachmentType.AURA)) { this.staticText = "Enchanted creature can block only creatures with flying"; } else { - this.staticText = "Equiped creature can block only creatures with flying"; + this.staticText = "Equipped creature can block only creatures with flying"; } } diff --git a/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedAttachedEffect.java b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedAttachedEffect.java index acf01412d8a..3d498d45856 100644 --- a/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedAttachedEffect.java @@ -45,7 +45,7 @@ public class CantBeBlockedAttachedEffect extends RestrictionEffect { if (attachmentType.equals(AttachmentType.AURA)) { this.staticText = "Enchanted creature can't be blocked"; } else { - this.staticText = "Equiped creature can't be blocked"; + this.staticText = "Equipped creature can't be blocked"; } } diff --git a/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAttachedEffect.java b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAttachedEffect.java index 576062930fc..967ffdce1e1 100644 --- a/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/combat/CantBeBlockedByCreaturesAttachedEffect.java @@ -48,9 +48,9 @@ public class CantBeBlockedByCreaturesAttachedEffect extends RestrictionEffect { this.filter = filter; StringBuilder sb = new StringBuilder(); if (attachmentType.equals(AttachmentType.AURA)) { - sb.append("Enchanted"); + sb.append("Enchanted "); } else { - sb.append("Equipped"); + sb.append("Equipped "); } staticText = sb.append("creature can't be blocked ") .append(filter.getMessage().startsWith("except by") ? "":"by ").append(filter.getMessage()).toString(); From c1fa3422fd7ea373baf352b42c8b670f717f271e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Tue, 2 Jun 2015 23:37:15 +0200 Subject: [PATCH 037/102] * Mycosynth Golem - Fixed not working second ability. --- .../mage/sets/fifthdawn/MycosynthGolem.java | 89 ++++++++++--------- .../mage/sets/magic2015/ChiefEngineer.java | 3 - .../abilities/other/MycosynthGolemTest.java | 17 +++- Mage/src/mage/abilities/AbilityImpl.java | 16 +++- .../keyword/AffinityForArtifactsAbility.java | 2 +- 5 files changed, 74 insertions(+), 53 deletions(-) diff --git a/Mage.Sets/src/mage/sets/fifthdawn/MycosynthGolem.java b/Mage.Sets/src/mage/sets/fifthdawn/MycosynthGolem.java index acd7225b6b0..084fbf7fc3e 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/MycosynthGolem.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/MycosynthGolem.java @@ -29,27 +29,39 @@ package mage.sets.fifthdawn; import java.util.UUID; import mage.MageInt; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.AffinityForArtifactsAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.SubLayer; import mage.constants.Zone; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; -import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.game.stack.Spell; +import mage.game.stack.StackObject; +import mage.players.Player; /** * * @author jeffwadsworth */ public class MycosynthGolem extends CardImpl { + + private static final FilterSpell filter = new FilterSpell("Artifact creature spells you cast"); + static { + filter.add(new CardTypePredicate(CardType.ARTIFACT)); + filter.add(new CardTypePredicate(CardType.CREATURE)); + } + public MycosynthGolem(UUID ownerId) { super(ownerId, 137, "Mycosynth Golem", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{11}"); this.expansionSetCode = "5DN"; @@ -62,7 +74,8 @@ public class MycosynthGolem extends CardImpl { this.addAbility(new AffinityForArtifactsAbility()); // Artifact creature spells you cast have affinity for artifacts. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MycosynthGolemEffect())); + this.addAbility(new SimpleStaticAbility( + Zone.BATTLEFIELD, new MycosynthGolemGainAbilitySpellsEffect(new AffinityForArtifactsAbility(), filter))); } @@ -76,56 +89,46 @@ public class MycosynthGolem extends CardImpl { } } -class MycosynthGolemEffect extends ReplacementEffectImpl { +class MycosynthGolemGainAbilitySpellsEffect extends ContinuousEffectImpl { - public MycosynthGolemEffect() { - super(Duration.WhileOnBattlefield, Outcome.Benefit); - staticText = "Artifact creature spells you cast have affinity for artifacts"; + private final Ability ability; + private final FilterSpell filter; + + public MycosynthGolemGainAbilitySpellsEffect(Ability ability, FilterSpell filter) { + super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); + this.ability = ability; + this.filter = filter; + staticText = filter.getMessage() + " have " + ability.getRule(); } - public MycosynthGolemEffect(final MycosynthGolemEffect effect) { + public MycosynthGolemGainAbilitySpellsEffect(final MycosynthGolemGainAbilitySpellsEffect effect) { super(effect); + this.ability = effect.ability; + this.filter = effect.filter; } @Override - public MycosynthGolemEffect copy() { - return new MycosynthGolemEffect(this); + public MycosynthGolemGainAbilitySpellsEffect copy() { + return new MycosynthGolemGainAbilitySpellsEffect(this); } @Override public boolean apply(Game game, Ability source) { - return true; - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - MageObject object = game.getObject(event.getSourceId()); - if (object != null) { - Card card = (Card) object; - Ability ability = new AffinityForArtifactsAbility(); - game.getState().addOtherAbility(card, ability); - ability.setControllerId(source.getControllerId()); - ability.setSourceId(card.getId()); - game.getState().addAbility(ability, source.getSourceId(), card); - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.CAST_SPELL; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if ((event.getType() == GameEvent.EventType.CAST_SPELL) - && event.getPlayerId() == source.getControllerId()) { - MageObject spellObject = game.getObject(event.getSourceId()); - if (spellObject != null - && spellObject.getCardType().contains(CardType.CREATURE) - && spellObject.getCardType().contains(CardType.ARTIFACT)) { - return true; + Player player = game.getPlayer(source.getControllerId()); + Permanent permanent = game.getPermanent(source.getSourceId()); + if (player != null && permanent != null) { + for (StackObject stackObject : game.getStack()) { + // only spells cast, so no copies of spells + if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.getControllerId().equals(source.getControllerId())) { + Spell spell = (Spell) stackObject; + if (filter.match(spell, game)) { + if (!spell.getAbilities().contains(ability)) { + game.getState().addOtherAbility(spell.getCard(), ability); + } + } + } } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/magic2015/ChiefEngineer.java b/Mage.Sets/src/mage/sets/magic2015/ChiefEngineer.java index 32f86d943ac..516de3d0fd9 100644 --- a/Mage.Sets/src/mage/sets/magic2015/ChiefEngineer.java +++ b/Mage.Sets/src/mage/sets/magic2015/ChiefEngineer.java @@ -27,14 +27,12 @@ */ package mage.sets.magic2015; -import java.util.Iterator; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.ConvokeAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; @@ -43,7 +41,6 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SubLayer; import mage.constants.Zone; -import mage.filter.FilterCard; import mage.filter.FilterSpell; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java index 69cefe72618..0de986e4214 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/MycosynthGolemTest.java @@ -30,7 +30,8 @@ package org.mage.test.cards.abilities.other; import mage.constants.PhaseStep; import mage.constants.Zone; -import org.junit.Ignore; +import mage.game.permanent.Permanent; +import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -50,10 +51,11 @@ public class MycosynthGolemTest extends CardTestPlayerBase { * */ - @Ignore // at this time player.getPlayable() does not account for spells that gain abilities + // @Ignore // at this time player.getPlayable() does not account for spells that gain abilities @Test public void testSpellsAffinity() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); addCard(Zone.BATTLEFIELD, playerA, "Mycosynth Golem"); addCard(Zone.HAND, playerA, "Alpha Myr"); @@ -64,6 +66,17 @@ public class MycosynthGolemTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Alpha Myr", 1); assertHandCount(playerA, "Alpha Myr", 0); + + Permanent mountain = getPermanent("Mountain", playerA); + Permanent forest = getPermanent("Forest", playerA); + int tappedLands = 0; + if (mountain.isTapped()) { + tappedLands++; + } + if (forest.isTapped()) { + tappedLands++; + } + Assert.assertEquals("only one land may be tapped because the cost reduction", 1, tappedLands); } diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index 844e6de7c48..f9847aa8b7e 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -352,10 +352,18 @@ public abstract class AbilityImpl implements Ability { //20100716 - 601.2e if (sourceObject != null) { sourceObject.adjustCosts(this, game); - for (Ability ability : sourceObject.getAbilities()) { - if (ability instanceof AdjustingSourceCosts) { - ((AdjustingSourceCosts)ability).adjustCosts(this, game); - } + if (sourceObject instanceof Card) { + for (Ability ability : ((Card)sourceObject).getAbilities(game)) { + if (ability instanceof AdjustingSourceCosts) { + ((AdjustingSourceCosts)ability).adjustCosts(this, game); + } + } + } else { + for (Ability ability : sourceObject.getAbilities()) { + if (ability instanceof AdjustingSourceCosts) { + ((AdjustingSourceCosts)ability).adjustCosts(this, game); + } + } } } diff --git a/Mage/src/mage/abilities/keyword/AffinityForArtifactsAbility.java b/Mage/src/mage/abilities/keyword/AffinityForArtifactsAbility.java index 6bb24081cb3..2f2a1729280 100644 --- a/Mage/src/mage/abilities/keyword/AffinityForArtifactsAbility.java +++ b/Mage/src/mage/abilities/keyword/AffinityForArtifactsAbility.java @@ -65,7 +65,7 @@ public class AffinityForArtifactsAbility extends SimpleStaticAbility implements @Override public String getRule() { - return "Affinity for artifacts"; + return "affinity for artifacts (This spell costs {1} less to cast for each artifact you control.)"; } @Override From a5bdff68262ed65cefc639fcd0c5a4c66c7dea30 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 00:10:02 +0200 Subject: [PATCH 038/102] Added a test. --- .../sets/bornofthegods/SatyrFiredancer.java | 1 - .../oneshot/damage/SatyrFiredancerTest.java | 20 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/bornofthegods/SatyrFiredancer.java b/Mage.Sets/src/mage/sets/bornofthegods/SatyrFiredancer.java index 7ba3689bbd8..7706bfac8ef 100644 --- a/Mage.Sets/src/mage/sets/bornofthegods/SatyrFiredancer.java +++ b/Mage.Sets/src/mage/sets/bornofthegods/SatyrFiredancer.java @@ -36,7 +36,6 @@ import mage.abilities.Ability; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java index b97cd37240f..e98e58bc6be 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/damage/SatyrFiredancerTest.java @@ -29,7 +29,6 @@ package org.mage.test.cards.abilities.oneshot.damage; import mage.constants.PhaseStep; import mage.constants.Zone; -import mage.counters.CounterType; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -83,4 +82,23 @@ public class SatyrFiredancerTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Silvercoat Lion", 1); } + + @Test + public void testDamageFromOtherCreature() { + // Whenever an instant or sorcery spell you control deals damage to an opponent, Satyr Firedancer deals that much damage to target creature that player controls. + addCard(Zone.BATTLEFIELD, playerA, "Satyr Firedancer"); + + // {T}: Prodigal Pyromancer deals 1 damage to target creature or player. + addCard(Zone.BATTLEFIELD, playerA, "Prodigal Pyromancer", 1); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: {source} deals", playerB); + addTarget(playerA, playerB); + + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 19); + + } } From 022d8790fc1fad806b8a3808ff6ebd85fb4d2cf3 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 00:11:15 +0200 Subject: [PATCH 039/102] Saved skill filter setting of active tables list. --- Mage.Client/src/main/java/mage/client/table/TablesPanel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index d553b6d3241..c9440f01e6e 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -146,7 +146,8 @@ public class TablesPanel extends javax.swing.JPanel { filterButtons = new JToggleButton[] {btnStateWaiting, btnStateActive, btnStateFinished, btnTypeMatch, btnTypeTourneyConstructed, btnTypeTourneyLimited, - btnFormatBlock, btnFormatStandard, btnFormatModern, btnFormatLegacy, btnFormatVintage, btnFormatCommander, btnFormatTinyLeader, btnFormatLimited, btnFormatOther}; + btnFormatBlock, btnFormatStandard, btnFormatModern, btnFormatLegacy, btnFormatVintage, btnFormatCommander, btnFormatTinyLeader, btnFormatLimited, btnFormatOther, + btnSkillBeginner, btnSkillCasual, btnSkillSerious }; JComponent[] components = new JComponent[] {chatPanel, jSplitPane1, jScrollPane1, jScrollPane2, topPanel, jPanel3}; for (JComponent component : components) { From 79183c3e4dd48c4c2a4765f15b9f0bcec8ecb749 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 00:17:46 +0200 Subject: [PATCH 040/102] * Forsaken City - Fixed mana ability only producing colorless mana. --- Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java b/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java index 6c811609580..0a39f721f27 100644 --- a/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java +++ b/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java @@ -34,7 +34,7 @@ import mage.abilities.costs.common.ExileFromHandCost; import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect; import mage.abilities.effects.common.UntapSourceEffect; -import mage.abilities.mana.ColorlessManaAbility; +import mage.abilities.mana.AnyColorManaAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; @@ -45,7 +45,7 @@ import mage.target.common.TargetCardInHand; /** * - * @author anonymous + * @author Luna Skyrise */ public class ForsakenCity extends CardImpl { @@ -57,10 +57,12 @@ public class ForsakenCity extends CardImpl { // Forsaken City doesn't untap during your untap step. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DontUntapInControllersUntapStepSourceEffect())); + // At the beginning of your upkeep, you may exile a card from your hand. If you do, untap Forsaken City. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DoIfCostPaid(new UntapSourceEffect(), new ExileFromHandCost(new TargetCardInHand(filter))), TargetController.YOU, true)); - // {tap}: Add one mana of any color to your mana pool. - this.addAbility(new ColorlessManaAbility()); + + // {T}: Add one mana of any color to your mana pool. + this.addAbility(new AnyColorManaAbility()); } public ForsakenCity(final ForsakenCity card) { From bafd27f5b3b6a232cf226347a41d0534967e8b89 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 17:05:59 +0200 Subject: [PATCH 041/102] Identifier are shown for Looked at and revealed cards view --- .../main/java/mage/client/game/GamePanel.java | 4 +-- .../mage/client/util/gui/GuiDisplayUtil.java | 1 - Mage.Common/src/mage/view/CardView.java | 35 ++++++------------- Mage.Common/src/mage/view/CardsView.java | 2 +- Mage.Common/src/mage/view/CommanderView.java | 2 +- Mage.Common/src/mage/view/DeckView.java | 4 +-- Mage.Common/src/mage/view/DraftPickView.java | 4 +-- Mage.Common/src/mage/view/ExileView.java | 2 +- Mage.Common/src/mage/view/GameView.java | 2 +- Mage.Common/src/mage/view/LookedAtView.java | 2 +- Mage.Common/src/mage/view/PermanentView.java | 2 +- Mage.Common/src/mage/view/PlayerView.java | 4 +-- Mage.Common/src/mage/view/RevealedView.java | 6 ++-- Mage.Common/src/mage/view/SimpleCardView.java | 10 +++++- .../src/mage/view/SimpleCardsView.java | 4 +-- .../mage/server/game/GameSessionPlayer.java | 2 +- .../mage/server/game/GameSessionWatcher.java | 2 +- .../src/mage/sets/tenthedition/Peek.java | 10 +++--- .../keyword/AffinityForLandTypeAbility.java | 8 ++--- Mage/src/mage/game/stack/Spell.java | 8 ++++- 20 files changed, 58 insertions(+), 56 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 6021b18fbe2..2e7483b9c16 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -801,7 +801,7 @@ public final class GamePanel extends javax.swing.JPanel { ShowCardsDialog newReveal = new ShowCardsDialog(); revealed.put(reveal.getName(), newReveal); } - revealed.get(reveal.getName()).loadCards("Revealed " + reveal.getName(), CardsViewUtil.convertSimple(reveal.getCards(), loadedCards), bigCard, Config.dimensions, gameId, false); + revealed.get(reveal.getName()).loadCards("Revealed " + reveal.getName(), reveal.getCards(), bigCard, Config.dimensions, gameId, false); } } @@ -814,7 +814,7 @@ public final class GamePanel extends javax.swing.JPanel { ShowCardsDialog newLookedAt = new ShowCardsDialog(); lookedAt.put(looked.getName(), newLookedAt); } - lookedAt.get(looked.getName()).loadCards("Looked at by " + looked.getName(), CardsViewUtil.convertSimple(looked.getCards(), loadedCards), bigCard, Config.dimensions, gameId, false); + lookedAt.get(looked.getName()).loadCards("Looked at by " + looked.getName(), looked.getCards(), bigCard, Config.dimensions, gameId, false); } } diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java index 0c178372735..8252189c8c0 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java @@ -13,7 +13,6 @@ import org.mage.card.arcane.UI; import javax.swing.*; import java.awt.*; import java.util.ArrayList; -import mage.client.dialog.MageDialog; import mage.constants.Rarity; public class GuiDisplayUtil { diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 85b6b1df8cc..5829fa3a3bf 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -114,20 +114,19 @@ public class CardView extends SimpleCardView { protected boolean isPlayable; protected boolean isChoosable; protected boolean selected; - protected boolean canAttack; - protected boolean gameObject; + protected boolean canAttack; public CardView(Card card) { - this(card, null, null, false); + this(card, null, false); } public CardView(Card card, UUID cardId) { - this(card, null, null, false); + this(card, null, false); this.id = cardId; } public CardView(Card card, Game game, UUID cardId) { - this(card, game, null, false); + this(card, game, false); this.id = cardId; } @@ -135,15 +134,12 @@ public class CardView extends SimpleCardView { * * @param card * @param game - * @param cardId not used? * @param controlled is the card view created for the card controller - used for morph / face down cards to know which player may see information for the card */ - public CardView(Card card, Game game, UUID cardId, boolean controlled) { - super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode()); + public CardView(Card card, Game game, boolean controlled) { + super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null); // no information available for face down cards as long it's not a controlled face down morph card - // TODO: Better handle this in Framework (but currently I'm not sure how to do it there) LevelX2 - this.gameObject = game != null; - + // TODO: Better handle this in Framework (but currently I'm not sure how to do it there) LevelX2 if (game != null && card.isFaceDown(game)) { this.fillEmpty(card, controlled); if (card instanceof Spell) { @@ -301,7 +297,7 @@ public class CardView extends SimpleCardView { } public CardView(MageObject object) { - super(object.getId(), "", 0, false, ""); + super(object.getId(), "", 0, false, "", true); this.name = object.getName(); this.displayName = object.getName(); if (object instanceof Permanent) { @@ -345,11 +341,12 @@ public class CardView extends SimpleCardView { } protected CardView() { - super(null, "", 0, false, ""); + super(null, "", 0, false, "", true); } public CardView(EmblemView emblem) { this(true); + this.gameObject = true; this.id = emblem.getId(); this.mageObjectType = MageObjectType.EMBLEM; this.name = emblem.getName(); @@ -368,13 +365,6 @@ public class CardView extends SimpleCardView { fillEmpty(null, false); } - - public CardView(String name) { - this(true); - this.name = name; - this.displayName = name; - } - private void fillEmpty(Card card, boolean controlled) { this.name = "Face Down"; this.displayName = name; @@ -736,8 +726,5 @@ public class CardView extends SimpleCardView { public void setCanAttack(boolean canAttack) { this.canAttack = canAttack; } - - public boolean isGameObject() { - return gameObject; - } + } diff --git a/Mage.Common/src/mage/view/CardsView.java b/Mage.Common/src/mage/view/CardsView.java index 57284013352..a39a9d6e007 100644 --- a/Mage.Common/src/mage/view/CardsView.java +++ b/Mage.Common/src/mage/view/CardsView.java @@ -66,7 +66,7 @@ public class CardsView extends LinkedHashMap { public CardsView(Game game, Collection cards) { for (Card card: cards) { - this.put(card.getId(), new CardView(card, game, null, false)); + this.put(card.getId(), new CardView(card, game, false)); } } diff --git a/Mage.Common/src/mage/view/CommanderView.java b/Mage.Common/src/mage/view/CommanderView.java index 46e012b374c..843a38ae2ce 100644 --- a/Mage.Common/src/mage/view/CommanderView.java +++ b/Mage.Common/src/mage/view/CommanderView.java @@ -40,7 +40,7 @@ import mage.game.command.Commander; public class CommanderView extends CardView implements CommandObjectView, Serializable{ public CommanderView(Commander commander, Card sourceCard, Game game) { - super(sourceCard, game, null, false); + super(sourceCard, game, false); this.mageObjectType = MageObjectType.COMMANDER; } } diff --git a/Mage.Common/src/mage/view/DeckView.java b/Mage.Common/src/mage/view/DeckView.java index dccd0f46b0f..1542ac4a80e 100644 --- a/Mage.Common/src/mage/view/DeckView.java +++ b/Mage.Common/src/mage/view/DeckView.java @@ -41,8 +41,8 @@ public class DeckView implements Serializable { public DeckView(Deck deck) { name = deck.getName(); - cards = new SimpleCardsView(deck.getCards()); - sideboard = new SimpleCardsView(deck.getSideboard()); + cards = new SimpleCardsView(deck.getCards(), false); + sideboard = new SimpleCardsView(deck.getSideboard(), false); } public String getName() { diff --git a/Mage.Common/src/mage/view/DraftPickView.java b/Mage.Common/src/mage/view/DraftPickView.java index 82ee5cc0996..b1bf2c22b59 100644 --- a/Mage.Common/src/mage/view/DraftPickView.java +++ b/Mage.Common/src/mage/view/DraftPickView.java @@ -44,8 +44,8 @@ public class DraftPickView implements Serializable { protected int timeout; public DraftPickView(DraftPlayer player, int timeout) { - this.booster = new SimpleCardsView(player.getBooster()); - this.picks = new SimpleCardsView(player.getDeck().getSideboard()); + this.booster = new SimpleCardsView(player.getBooster(), false); + this.picks = new SimpleCardsView(player.getDeck().getSideboard(), false); this.picking = player.isPicking(); this.timeout = timeout; } diff --git a/Mage.Common/src/mage/view/ExileView.java b/Mage.Common/src/mage/view/ExileView.java index b6c3ecc87bf..bd0c03752a3 100644 --- a/Mage.Common/src/mage/view/ExileView.java +++ b/Mage.Common/src/mage/view/ExileView.java @@ -47,7 +47,7 @@ public class ExileView extends CardsView { this.name = exileZone.getName(); this.id = exileZone.getId(); for (Card card: exileZone.getCards(game)) { - this.put(card.getId(), new CardView(card, game, card.getId(), false)); + this.put(card.getId(), new CardView(card, game, false)); } } diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index 467780abf1c..32b8be1bcd9 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -136,7 +136,7 @@ public class GameView implements Serializable { } else { // Spell - stack.put(stackObject.getId(), new CardView((Spell)stackObject, game, null, stackObject.getControllerId().equals(createdForPlayerId))); + stack.put(stackObject.getId(), new CardView((Spell)stackObject, game, stackObject.getControllerId().equals(createdForPlayerId))); checkPaid(stackObject.getId(), (Spell)stackObject); } //stackOrder.add(stackObject.getId()); diff --git a/Mage.Common/src/mage/view/LookedAtView.java b/Mage.Common/src/mage/view/LookedAtView.java index a7485e7120f..db1759c0c08 100644 --- a/Mage.Common/src/mage/view/LookedAtView.java +++ b/Mage.Common/src/mage/view/LookedAtView.java @@ -46,7 +46,7 @@ public class LookedAtView implements Serializable { public LookedAtView(String name, Cards cards, Game game) { this.name = name; for (Card card: cards.getCards(game)) { - this.cards.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode())); + this.cards.put(card.getId(), new CardView(card, game, card.getId())); } } diff --git a/Mage.Common/src/mage/view/PermanentView.java b/Mage.Common/src/mage/view/PermanentView.java index 4ce0246ea05..2b7fe476f00 100644 --- a/Mage.Common/src/mage/view/PermanentView.java +++ b/Mage.Common/src/mage/view/PermanentView.java @@ -62,7 +62,7 @@ public class PermanentView extends CardView { private final boolean attachedToPermanent; public PermanentView(Permanent permanent, Card card, UUID createdForPlayerId, Game game) { - super(permanent, game, null, permanent.getControllerId().equals(createdForPlayerId)); + super(permanent, game, permanent.getControllerId().equals(createdForPlayerId)); this.controlled = permanent.getControllerId().equals(createdForPlayerId); this.rules = permanent.getRules(game); this.tapped = permanent.isTapped(); diff --git a/Mage.Common/src/mage/view/PlayerView.java b/Mage.Common/src/mage/view/PlayerView.java index 343e30332c7..fb9e36ad62c 100644 --- a/Mage.Common/src/mage/view/PlayerView.java +++ b/Mage.Common/src/mage/view/PlayerView.java @@ -95,12 +95,12 @@ public class PlayerView implements Serializable { this.hasLeft = player.hasLeft(); for (Card card: player.getGraveyard().getCards(game)) { - graveyard.put(card.getId(), new CardView(card, game, card.getId(), false)); + graveyard.put(card.getId(), new CardView(card, game, false)); } for (ExileZone exileZone : game.getExile().getExileZones()) { for (Card card : exileZone.getCards(game)) { if (player.getId().equals(card.getOwnerId())) { - exile.put(card.getId(), new CardView(card, game, card.getId(), false)); // unnown if it's allowed to look under a face down card + exile.put(card.getId(), new CardView(card, game, false)); // unnown if it's allowed to look under a face down card } } } diff --git a/Mage.Common/src/mage/view/RevealedView.java b/Mage.Common/src/mage/view/RevealedView.java index 3f067267cda..d5f086e4101 100644 --- a/Mage.Common/src/mage/view/RevealedView.java +++ b/Mage.Common/src/mage/view/RevealedView.java @@ -40,12 +40,12 @@ import mage.game.Game; public class RevealedView implements Serializable { private final String name; - private final SimpleCardsView cards = new SimpleCardsView(); + private final CardsView cards = new CardsView(); public RevealedView(String name, Cards cards, Game game) { this.name = name; for (Card card: cards.getCards(game)) { - this.cards.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode())); + this.cards.put(card.getId(), new CardView(card, game, card.getId())); } } @@ -53,7 +53,7 @@ public class RevealedView implements Serializable { return name; } - public SimpleCardsView getCards() { + public CardsView getCards() { return cards; } } diff --git a/Mage.Common/src/mage/view/SimpleCardView.java b/Mage.Common/src/mage/view/SimpleCardView.java index daf216d72cc..fd8abe538c0 100644 --- a/Mage.Common/src/mage/view/SimpleCardView.java +++ b/Mage.Common/src/mage/view/SimpleCardView.java @@ -41,13 +41,18 @@ public class SimpleCardView implements Serializable { protected String tokenSetCode; protected int cardNumber; protected boolean usesVariousArt; + protected boolean gameObject; public SimpleCardView(UUID id, String expansionSetCode, int cardNumber, boolean usesVariousArt, String tokenSetCode) { + this(id, expansionSetCode, cardNumber, usesVariousArt, tokenSetCode, false); + } + public SimpleCardView(UUID id, String expansionSetCode, int cardNumber, boolean usesVariousArt, String tokenSetCode, boolean isGameObject) { this.id = id; this.expansionSetCode = expansionSetCode; this.cardNumber = cardNumber; this.usesVariousArt = usesVariousArt; this.tokenSetCode = tokenSetCode; + this.gameObject = isGameObject; } public UUID getId() { @@ -69,5 +74,8 @@ public class SimpleCardView implements Serializable { public String getTokenSetCode() { return tokenSetCode; } - + + public boolean isGameObject() { + return gameObject; + } } diff --git a/Mage.Common/src/mage/view/SimpleCardsView.java b/Mage.Common/src/mage/view/SimpleCardsView.java index d3763e4eedd..909d0ee2866 100644 --- a/Mage.Common/src/mage/view/SimpleCardsView.java +++ b/Mage.Common/src/mage/view/SimpleCardsView.java @@ -42,9 +42,9 @@ public class SimpleCardsView extends LinkedHashMap { public SimpleCardsView() {} - public SimpleCardsView(Collection cards) { + public SimpleCardsView(Collection cards, boolean isGameObject) { for (Card card: cards) { - this.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode())); + this.put(card.getId(), new SimpleCardView(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), isGameObject)); } } diff --git a/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java b/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java index e2cd374f6f4..2cfff99a561 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java +++ b/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java @@ -237,7 +237,7 @@ public class GameSessionPlayer extends GameSessionWatcher { Map handCards = new HashMap<>(); for (UUID controlledPlayerId : player.getPlayersUnderYourControl()) { Player opponent = game.getPlayer(controlledPlayerId); - handCards.put(opponent.getName(), new SimpleCardsView(opponent.getHand().getCards(game))); + handCards.put(opponent.getName(), new SimpleCardsView(opponent.getHand().getCards(game), true)); } gameView.setOpponentHands(handCards); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java b/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java index f269b87f0e3..e1db1e08d25 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java +++ b/Mage.Server/src/main/java/mage/server/game/GameSessionWatcher.java @@ -142,7 +142,7 @@ public class GameSessionWatcher { Map handCards = new HashMap<>(); for (Player player: game.getPlayers().values()) { if (player.hasUserPermissionToSeeHand(userId)) { - handCards.put(player.getName(), new SimpleCardsView(player.getHand().getCards(game))); + handCards.put(player.getName(), new SimpleCardsView(player.getHand().getCards(game), true)); gameView.setWatchedHands(handCards); } } diff --git a/Mage.Sets/src/mage/sets/tenthedition/Peek.java b/Mage.Sets/src/mage/sets/tenthedition/Peek.java index 1ef7f16dbef..7f60d71d82a 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/Peek.java +++ b/Mage.Sets/src/mage/sets/tenthedition/Peek.java @@ -28,6 +28,7 @@ package mage.sets.tenthedition; import java.util.UUID; +import mage.MageObject; import mage.constants.CardType; import mage.constants.Rarity; @@ -50,10 +51,10 @@ public class Peek extends CardImpl { super(ownerId, 94, "Peek", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{U}"); this.expansionSetCode = "10E"; - // Look at target player's hand. this.getSpellAbility().addEffect(new PeekEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); + // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } @@ -71,7 +72,7 @@ public class Peek extends CardImpl { class PeekEffect extends OneShotEffect { PeekEffect() { super(Outcome.Detriment); - staticText = "Look at target player's hand"; + staticText = "look at target player's hand"; } PeekEffect(final PeekEffect effect) { @@ -82,8 +83,9 @@ class PeekEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(targetPointer.getFirst(game, source)); - if (player != null && controller != null) { - controller.lookAtCards("Peek", player.getHand(), game); + MageObject sourceObject = source.getSourceObject(game); + if (player != null && controller != null && sourceObject != null) { + controller.lookAtCards(sourceObject.getIdName(), player.getHand(), game); } return true; } diff --git a/Mage/src/mage/abilities/keyword/AffinityForLandTypeAbility.java b/Mage/src/mage/abilities/keyword/AffinityForLandTypeAbility.java index 451aac0c7df..a0fadfd3787 100644 --- a/Mage/src/mage/abilities/keyword/AffinityForLandTypeAbility.java +++ b/Mage/src/mage/abilities/keyword/AffinityForLandTypeAbility.java @@ -32,10 +32,8 @@ import mage.abilities.SpellAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.AdjustingSourceCosts; import mage.abilities.effects.common.AffinityEffect; -import mage.constants.CardType; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; import mage.util.CardUtil; @@ -50,12 +48,14 @@ public class AffinityForLandTypeAbility extends SimpleStaticAbility implements A private final FilterControlledPermanent filter; String text; + String landType; public AffinityForLandTypeAbility(String landType, String text) { super(Zone.OUTSIDE, new AffinityEffect(getFilter(landType))); this.filter = getFilter(landType); setRuleAtTheTop(true); this.text = text; + this.landType = landType; } private static FilterControlledPermanent getFilter(String landType) { @@ -78,7 +78,7 @@ public class AffinityForLandTypeAbility extends SimpleStaticAbility implements A @Override public String getRule() { - return "Affinity for " + text; + return "Affinity for " + text + " (This spell costs 1 less to cast for each " + landType + " you control.)"; } @Override @@ -90,4 +90,4 @@ public class AffinityForLandTypeAbility extends SimpleStaticAbility implements A } } } -} \ No newline at end of file +} diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index 03c01660280..7fe9215ad07 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -343,7 +343,13 @@ public class Spell extends StackObjImpl implements Card { @Override public String getIdName() { - return getName() + " ["+getId().toString().substring(0,3) +"]"; + String idName; + if (card != null) { + idName = card.getId().toString().substring(0,3); + } else { + idName = getId().toString().substring(0,3); + } + return getName() + " ["+idName+"]"; } @Override From 59c50436ace6d5d475728ca3ed2cc4a61110f5de Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 17:06:46 +0200 Subject: [PATCH 042/102] Minor change. --- .../common/search/SearchLibraryPutOnLibraryEffect.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java index 33e6558d8e1..65e05e65846 100644 --- a/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java +++ b/Mage/src/mage/abilities/effects/common/search/SearchLibraryPutOnLibraryEffect.java @@ -78,22 +78,22 @@ public class SearchLibraryPutOnLibraryEffect extends SearchEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - MageObject sourceObject = game.getObject(source.getSourceId()); + MageObject sourceObject = source.getSourceObject(game); if (controller == null || sourceObject == null) { return false; } if (controller.searchLibrary(target, game)) { List cards = new ArrayList<>(); - for (UUID cardId: (List)target.getTargets()) { + for (UUID cardId: target.getTargets()) { Card card = controller.getLibrary().remove(cardId, game); if (card != null) { cards.add(card); } } Cards foundCards = new CardsImpl(); - foundCards.addAll(cards); + foundCards.addAll(target.getTargets()); if (reveal) { - controller.revealCards(sourceObject.getName(), foundCards, game); + controller.revealCards(sourceObject.getIdName(), foundCards, game); } if (forceShuffle) { controller.shuffleLibrary(game); @@ -117,7 +117,7 @@ public class SearchLibraryPutOnLibraryEffect extends SearchEffect { StringBuilder sb = new StringBuilder(); sb.append("Search your library for a ").append(target.getTargetName()); if (reveal) { - sb.append("and reveal that card. Shuffle"); + sb.append(" and reveal that card. Shuffle"); } else { sb.append(", then shuffle"); } From f63fae06de18747c7f12b1542f0d2e55080047b1 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 17:08:01 +0200 Subject: [PATCH 043/102] Some changes to AI player. --- .../src/mage/player/ai/ComputerPlayer6.java | 48 ++++++++++++------- .../src/mage/player/ai/ComputerPlayer7.java | 18 +++---- .../src/mage/player/ai/SimulatedPlayer2.java | 4 ++ .../player/ai/ma/ArtificialScoringSystem.java | 2 +- .../java/mage/player/ai/ComputerPlayer.java | 4 ++ 5 files changed, 49 insertions(+), 27 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java index abb13ee7740..da899f41f40 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java @@ -63,6 +63,7 @@ import mage.target.Targets; import java.io.File; import java.util.*; import java.util.concurrent.*; +import mage.constants.AbilityType; /** * @@ -86,7 +87,8 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { protected Set actionCache; private static final List optimizers = new ArrayList<>(); protected int lastLoggedTurn = 0; - + Random random = new Random(); + protected static final String BLANKS = "..............................................."; static { optimizers.add(new LevelUpOptimizer()); optimizers.add(new EquipOptimizer()); @@ -436,8 +438,12 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { }); pool.execute(task); try { - logger.debug("maxThink: " + maxThink + " seconds"); - return task.get(maxThink, TimeUnit.SECONDS); + int maxSeconds = maxThink; + if (!ALLOW_INTERRUPT) { + maxSeconds = 3600; + } + logger.debug("maxThink: " + maxSeconds + " seconds "); + return task.get(maxSeconds, TimeUnit.SECONDS); } catch (TimeoutException e) { logger.info("simulating - timed out"); task.cancel(true); @@ -461,7 +467,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { } Game game = node.getGame(); int val; - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); val = GameStateEvaluator2.evaluate(playerId, game); logger.trace("interrupted - " + val); @@ -505,7 +511,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { } protected int simulatePriority(SimulationNode2 node, Game game, int depth, int alpha, int beta) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.info("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -522,7 +528,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { int counter = 0; for (Ability action : allActions) { counter++; - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.info("Sim Prio [" + depth + "] -- interrupted"); break; @@ -537,16 +543,24 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { } if (!sim.gameOver(null) && action.isUsesStack()) { // only pass if the last action uses the stack - sim.getPlayer(currentPlayer.getId()).pass(game); - sim.getPlayerList().getNext(); + UUID nextPlayerId = sim.getPlayerList().get(); + do { + sim.getPlayer(nextPlayerId).pass(game); + nextPlayerId = sim.getPlayerList().getNext(); + } while (nextPlayerId != this.getId()); } SimulationNode2 newNode = new SimulationNode2(node, sim, action, depth, currentPlayer.getId()); - logger.trace(new StringBuilder("Sim Prio [").append(depth).append("]#").append(counter).append(" -- newNode (").append(action.toString()).append(") ").append(newNode.hashCode()).append(" parent node ").append(node.hashCode())); - // int testVal = GameStateEvaluator2.evaluate(currentPlayer.getId(), sim); - sim.checkStateAndTriggered(); - int val = addActions(newNode, depth - 1, alpha, beta); - + int val; + if (action instanceof PassAbility) { + // Stop to simulate deeper if PassAbility + val = GameStateEvaluator2.evaluate(this.getId(), sim); +// logger.info("evaluate = " + val ); + } else { + val = addActions(newNode, depth - 1, alpha, beta); +// logger.info("addAction = " + val ); + } + logger.debug("Sim Prio " + BLANKS.substring(0, 2 + (maxDepth-depth) * 3)+ "["+depth+"]#"+counter+" <" + val +"> - ("+action.toString()+") "+newNode.hashCode()+" parent node "+node.hashCode()); if (logger.isInfoEnabled() && depth == maxDepth) { StringBuilder sb = new StringBuilder("Sim Prio [").append(depth).append("] #").append(counter) .append(" <").append(val).append("> (").append(action) @@ -564,10 +578,10 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { } if (currentPlayer.getId().equals(playerId)) { - if (action instanceof PassAbility) { - val = val -15; // passivity penalty + if (depth == maxDepth && action instanceof PassAbility) { + val = val - PASSIVITY_PENALTY; // passivity penalty } - if (val > alpha) { + if (val > alpha || (depth == maxDepth && val == alpha && random.nextBoolean())) { // Adding random for equal value to get change sometimes alpha = val; bestNode = newNode; bestNode.setScore(val); @@ -1311,7 +1325,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { private boolean checkForRepeatedAction(Game sim, SimulationNode2 node, Ability action, UUID playerId) { // pass or casting two times a spell multiple times on hand is ok - if (action instanceof PassAbility || action instanceof SpellAbility) { + if (action instanceof PassAbility || action instanceof SpellAbility || action.getAbilityType().equals(AbilityType.MANA)) { return false; } int newVal = GameStateEvaluator2.evaluate(playerId, sim); diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java index 4b443351f06..6c28193b776 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java @@ -244,7 +244,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { logger.trace("Add Action [" + depth + "] " + node.getAbilities().toString() + " a: " + alpha + " b: " + beta); } Game game = node.getGame(); - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -349,7 +349,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { protected int simulateCombat(Game game, SimulationNode2 node, int depth, int alpha, int beta, boolean counter) { Integer val = null; - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -395,7 +395,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { protected int simulateAttackers(Game game, SimulationNode2 node, UUID attackerId, int depth, int alpha, int beta, boolean counter) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -472,7 +472,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } protected int simulateBlockers(Game game, SimulationNode2 node, UUID defenderId, int depth, int alpha, int beta, boolean counter) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -556,7 +556,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } /*protected int simulateCounterAttack(Game game, SimulationNode2 node, int depth, int alpha, int beta) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUP && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -583,7 +583,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { }*/ protected void simulateStep(Game game, Step step) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return; @@ -603,7 +603,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } protected void finishCombat(Game game) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return; @@ -614,7 +614,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } protected int simulatePostCombatMain(Game game, SimulationNode2 node, int depth, int alpha, int beta) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return GameStateEvaluator2.evaluate(playerId, game); @@ -632,7 +632,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { } protected void simulateToEnd(Game game) { - if (Thread.interrupted()) { + if (ALLOW_INTERRUPT && Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug("interrupted"); return; diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java index d58a47dbaa0..40e947230c6 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/SimulatedPlayer2.java @@ -46,6 +46,7 @@ import mage.abilities.costs.mana.VariableManaCost; import mage.abilities.effects.Effect; import mage.cards.Card; import mage.choices.Choice; +import mage.constants.AbilityType; import mage.game.Game; import mage.game.combat.Combat; import mage.game.events.GameEvent; @@ -124,6 +125,9 @@ public class SimulatedPlayer2 extends ComputerPlayer { List playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer); playables = filterAbilities(game, playables, suggested); for (Ability ability: playables) { + if (ability.getAbilityType().equals(AbilityType.MANA)) { + continue; + } List options = game.getPlayer(playerId).getPlayableOptions(ability, game); options = filterOptions(game, options, ability, suggested); options = optimizeOptions(game, options, ability); diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java index 5e5f1542bc5..4c008d09ff8 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ma/ArtificialScoringSystem.java @@ -129,7 +129,7 @@ public class ArtificialScoringSystem { if (permanent.getCardType().contains(CardType.CREATURE)) { return -100; } else if (permanent.getCardType().contains(CardType.LAND)) { - return -1; + return -20; // means probably no mana available (should be greater than passivity penalty } else { return -2; } diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index df0f14580ba..2208d111554 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -160,6 +160,10 @@ import org.apache.log4j.Logger; public class ComputerPlayer extends PlayerImpl implements Player { private transient final static Logger log = Logger.getLogger(ComputerPlayer.class); + + protected int PASSIVITY_PENALTY = 5; // Penalty value for doing nothing if some actions are availble + protected boolean ALLOW_INTERRUPT = true; // change this for test purposes to switch off interrupts while debugging + private transient Map unplayable = new TreeMap<>(); private transient List playableNonInstant = new ArrayList<>(); private transient List playableInstant = new ArrayList<>(); From 23bcbbb22a4963f7d876af8f3105ebd226b6a8f6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 17:11:36 +0200 Subject: [PATCH 044/102] Removed some unused references. --- Mage.Sets/src/mage/sets/futuresight/HomingSliver.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java b/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java index 1ec2b43aa51..35ec2cc9dcf 100644 --- a/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java +++ b/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java @@ -35,9 +35,7 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.common.DiscardSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffectImpl; -import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.abilities.keyword.CyclingAbility; -import mage.abilities.keyword.FlashbackAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -46,10 +44,8 @@ import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SubLayer; -import mage.constants.TimingRule; import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.game.Game; import mage.players.Player; From 56f41243ca525e0cc60f34704c52d6d30f77971f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 19:35:05 +0200 Subject: [PATCH 045/102] * Keranos, God of Storms - Fixed that it's triggered ability triggered not only on the first card drawn on the turn. --- .../journeyintonyx/KeranosGodOfStorms.java | 73 ++++++++++-- .../mage/sets/planeshift/ForsakenCity.java | 2 +- .../single/ths/KeranosGodOfStormsTest.java | 105 ++++++++++++++++++ 3 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/single/ths/KeranosGodOfStormsTest.java diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java b/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java index 8f18a5ff389..3175e4c5d0b 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java @@ -27,6 +27,9 @@ */ package mage.sets.journeyintonyx; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; import java.util.UUID; import mage.MageInt; import mage.abilities.TriggeredAbilityImpl; @@ -43,13 +46,17 @@ import mage.cards.CardImpl; import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; +import mage.constants.PhaseStep; import mage.constants.Rarity; +import mage.constants.WatcherScope; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreatureOrPlayer; +import mage.watchers.Watcher; +import mage.watchers.common.CardsDrawnDuringDrawStepWatcher; /** * @@ -77,7 +84,7 @@ public class KeranosGodOfStorms extends CardImpl { // Reveal the first card you draw on each of your turns. // Whenever you reveal a land card this way, draw a card. // Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player. - this.addAbility(new KeranosGodOfStormsTriggeredAbility()); + this.addAbility(new KeranosGodOfStormsTriggeredAbility(), new CardsDrawnDuringTurnWatcher()); } @@ -94,15 +101,12 @@ public class KeranosGodOfStorms extends CardImpl { class KeranosGodOfStormsTriggeredAbility extends TriggeredAbilityImpl { - private int lastTriggeredTurn; - KeranosGodOfStormsTriggeredAbility() { super(Zone.BATTLEFIELD, new InfoEffect(""), false); } KeranosGodOfStormsTriggeredAbility(final KeranosGodOfStormsTriggeredAbility ability) { super(ability); - this.lastTriggeredTurn = ability.lastTriggeredTurn; } @Override @@ -113,13 +117,16 @@ class KeranosGodOfStormsTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.DREW_CARD && event.getPlayerId().equals(this.getControllerId())) { - if (game.getActivePlayerId().equals(this.getControllerId()) && this.lastTriggeredTurn != game.getTurnNum()) { + if (game.getActivePlayerId().equals(this.getControllerId())) { + CardsDrawnDuringTurnWatcher watcher = (CardsDrawnDuringTurnWatcher) game.getState().getWatchers().get("CardsDrawnDuringTurn"); + if (watcher != null && watcher.getAmountCardsDrawn(event.getPlayerId()) != 1) { + return false; + } Card card = game.getCard(event.getTargetId()); Player controller = game.getPlayer(this.getControllerId()); - Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(this.getSourceId()); + Permanent sourcePermanent = (Permanent) getSourceObject(game); if (card != null && controller != null && sourcePermanent != null) { - lastTriggeredTurn = game.getTurnNum(); - controller.revealCards(sourcePermanent.getName(), new CardsImpl(card), game); + controller.revealCards(sourcePermanent.getIdName(), new CardsImpl(card), game); this.getTargets().clear(); this.getEffects().clear(); if (card.getCardType().contains(CardType.LAND)) { @@ -140,3 +147,53 @@ class KeranosGodOfStormsTriggeredAbility extends TriggeredAbilityImpl { return "Reveal the first card you draw on each of your turns. Whenever you reveal a land card this way, draw a card. Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player."; } } + +class CardsDrawnDuringTurnWatcher extends Watcher { + + private final Map amountOfCardsDrawnThisTurn = new HashMap<>(); + + public CardsDrawnDuringTurnWatcher() { + super("CardsDrawnDuringTurn", WatcherScope.GAME); + } + + public CardsDrawnDuringTurnWatcher(final CardsDrawnDuringTurnWatcher watcher) { + super(watcher); + for (Entry entry : watcher.amountOfCardsDrawnThisTurn.entrySet()) { + amountOfCardsDrawnThisTurn.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void watch(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DREW_CARD) { + UUID playerId = event.getPlayerId(); + if (playerId != null) { + Integer amount = amountOfCardsDrawnThisTurn.get(playerId); + if (amount == null) { + amount = 1; + } else { + amount++; + } + amountOfCardsDrawnThisTurn.put(playerId, amount); + } + } + } + + public int getAmountCardsDrawn(UUID playerId) { + Integer amount = amountOfCardsDrawnThisTurn.get(playerId); + if (amount != null) { + return amount; + } + return 0; + } + + @Override + public void reset() { + amountOfCardsDrawnThisTurn.clear(); + } + + @Override + public CardsDrawnDuringTurnWatcher copy() { + return new CardsDrawnDuringTurnWatcher(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java b/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java index 0a39f721f27..bcf4d89300b 100644 --- a/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java +++ b/Mage.Sets/src/mage/sets/planeshift/ForsakenCity.java @@ -62,7 +62,7 @@ public class ForsakenCity extends CardImpl { this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DoIfCostPaid(new UntapSourceEffect(), new ExileFromHandCost(new TargetCardInHand(filter))), TargetController.YOU, true)); // {T}: Add one mana of any color to your mana pool. - this.addAbility(new AnyColorManaAbility()); + this.addAbility(new AnyColorManaAbility()); } public ForsakenCity(final ForsakenCity card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/KeranosGodOfStormsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/KeranosGodOfStormsTest.java new file mode 100644 index 00000000000..99cc3c7b74f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/single/ths/KeranosGodOfStormsTest.java @@ -0,0 +1,105 @@ +/* + * 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.cards.single.ths; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class KeranosGodOfStormsTest extends CardTestPlayerBase { + + @Test + public void testKeranosNormal() { + // Reveal the first card you draw on each of your turns. + // Whenever you reveal a land card this way, draw a card. + // Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player. + addCard(Zone.BATTLEFIELD, playerB, "Keranos, God of Storms"); // {3}{U}{R} + // Look at target player's hand. + // Draw a card. + addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 3); + skipInitShuffling(); + + addTarget(playerB, playerA); // damage from Keranos trigger + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Keranos, God of Storms", 1); + + assertHandCount(playerB, "Silvercoat Lion", 1); // 1 Lion from the draw and 1 Lion from Peek + + assertLife(playerA, 17); + assertLife(playerB, 20); + + } + + /** + * Keranos, God of Storms, will look at the first card drawn after he is + * played, not the first card drawn each turn. + * + * My opponent, draws, plays Keranos, then plays Stroke of Genius. Keranos + * triggers on the first card for Stroke of Genius. + */ + @Test + public void testKeranosCastAfterFirstDraw() { + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + // Reveal the first card you draw on each of your turns. + // Whenever you reveal a land card this way, draw a card. + // Whenever you reveal a nonland card this way, Keranos deals 3 damage to target creature or player. + addCard(Zone.HAND, playerB, "Keranos, God of Storms"); // {3}{U}{R} + // Look at target player's hand. + // Draw a card. + addCard(Zone.HAND, playerB, "Peek"); + + addCard(Zone.LIBRARY, playerB, "Silvercoat Lion", 3); + skipInitShuffling(); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Keranos, God of Storms"); + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Peek", playerA); // you won't do damage because it's not the first draw this turn - Draw in draw phase was the first + addTarget(playerB, playerA); // not needed if it works correct + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerB, "Keranos, God of Storms", 1); + assertGraveyardCount(playerB, "Peek", 1); + + assertHandCount(playerB, "Silvercoat Lion", 2); // 1 Lion from the draw and 1 Lion from Peek + + assertLife(playerA, 20); + assertLife(playerB, 20); + + } + +} From f198dd2bc20bf5141c25643499927f7650fb104b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 19:40:54 +0200 Subject: [PATCH 046/102] * Grisly Salvage - Fixed that the move to hand effect was not optional. --- Mage.Sets/src/mage/sets/returntoravnica/GrislySalvage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/returntoravnica/GrislySalvage.java b/Mage.Sets/src/mage/sets/returntoravnica/GrislySalvage.java index 0b1ee4f9c85..e030085eb52 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/GrislySalvage.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/GrislySalvage.java @@ -56,7 +56,6 @@ public class GrislySalvage extends CardImpl { super(ownerId, 165, "Grisly Salvage", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{B}{G}"); this.expansionSetCode = "RTR"; - // Reveal the top five cards of your library. You may put a creature or land card from among them into your hand. Put the rest into your graveyard. this.getSpellAbility().addEffect(new GrislySalvageEffect()); } @@ -109,7 +108,8 @@ class GrislySalvageEffect extends OneShotEffect { if (!cards.isEmpty()) { controller.revealCards(sourceObject.getName(), cards, game); TargetCard target = new TargetCard(Zone.LIBRARY, filterPutInHand); - if (properCardFound && controller.choose(Outcome.DrawCard, cards, target, game)) { + if (properCardFound && controller.chooseUse(outcome, "Put a creature or land card from the revealed cards into your hand?", game) && + controller.choose(Outcome.DrawCard, cards, target, game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { controller.moveCards(card, Zone.LIBRARY, Zone.HAND, source, game); From eea43662e150524d909059469dac428a1254d0ad Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 20:16:58 +0200 Subject: [PATCH 047/102] Added test. --- .../mage/sets/darksteel/DarksteelCitadel.java | 4 + .../mage/sets/shadowmoor/FracturingGust.java | 2 - .../abilities/keywords/FlashbackTest.java | 78 +++++++++++++++++++ .../mage/game/permanent/PermanentImpl.java | 1 - 4 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java diff --git a/Mage.Sets/src/mage/sets/darksteel/DarksteelCitadel.java b/Mage.Sets/src/mage/sets/darksteel/DarksteelCitadel.java index 8fab79820b4..d230e02a283 100644 --- a/Mage.Sets/src/mage/sets/darksteel/DarksteelCitadel.java +++ b/Mage.Sets/src/mage/sets/darksteel/DarksteelCitadel.java @@ -44,7 +44,11 @@ public class DarksteelCitadel extends CardImpl { public DarksteelCitadel (UUID ownerId) { super(ownerId, 164, "Darksteel Citadel", Rarity.COMMON, new CardType[]{CardType.ARTIFACT, CardType.LAND}, null); this.expansionSetCode = "DST"; + + // Indestructible (Effects that say "destroy" don't destroy this land.) this.addAbility(IndestructibleAbility.getInstance()); + + // {T}: Add {1} to your mana pool. this.addAbility(new ColorlessManaAbility()); } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/FracturingGust.java b/Mage.Sets/src/mage/sets/shadowmoor/FracturingGust.java index b214901a10d..f8b990c0278 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/FracturingGust.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/FracturingGust.java @@ -51,7 +51,6 @@ public class FracturingGust extends CardImpl { super(ownerId, 227, "Fracturing Gust", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{2}{G/W}{G/W}{G/W}"); this.expansionSetCode = "SHM"; - // Destroy all artifacts and enchantments. You gain 2 life for each permanent destroyed this way. this.getSpellAbility().addEffect(new FracturingGustDestroyEffect()); } @@ -94,7 +93,6 @@ class FracturingGustDestroyEffect extends OneShotEffect { for (Permanent permanent: game.getState().getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { if (permanent.destroy(source.getSourceId(), game, false)) { ++destroyedPermanents; - } } game.applyEffects(); // needed in case a destroyed permanent did prevent life gain diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java new file mode 100644 index 00000000000..e3786359193 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FlashbackTest.java @@ -0,0 +1,78 @@ +/* + * 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.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class FlashbackTest extends CardTestPlayerBase { + + /** + * Fracturing Gust is bugged. In a match against Affinity, it worked + * properly when cast from hand. When I cast it from graveyard c/o + * Snapcaster Mage flashback, it destroyed my opponent's Darksteel Citadels, + * which it did not do when cast from my hand. + */ + @Test + public void testSnapcasterMageWithFracturingGust() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); + addCard(Zone.HAND, playerA, "Snapcaster Mage", 1); + + // Destroy all artifacts and enchantments. You gain 2 life for each permanent destroyed this way. + addCard(Zone.GRAVEYARD, playerA, "Fracturing Gust"); + + addCard(Zone.BATTLEFIELD, playerA, "Berserkers' Onslaught", 1); + addCard(Zone.BATTLEFIELD, playerB, "Darksteel Citadel", 1); + + + // When Snapcaster Mage enters the battlefield, target instant or sorcery card in your graveyard gains flashback until end of turn. The flashback cost is equal to its mana cost. + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Snapcaster Mage"); + setChoice(playerA, "Fracturing Gust"); + + activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Flashback {2}{G/W}{G/W}{G/W}"); // now snapcaster mage is died so -13/-13 + + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Snapcaster Mage", 1); + assertGraveyardCount(playerA, "Berserkers' Onslaught", 1); + + assertPermanentCount(playerB, "Darksteel Citadel", 1); + + assertExileCount("Fracturing Gust", 1); + } + +} diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 098313d6337..5bfc16c5428 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -30,7 +30,6 @@ package mage.game.permanent; import java.util.ArrayList; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; From 6b03658040460fc2afa735d60aefab1402d2464c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 20:25:41 +0200 Subject: [PATCH 048/102] * Vectis Dominator - Fixed wrong activation cost. --- Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java | 8 ++------ .../src/mage/sets/scarsofmirrodin/SteadyProgress.java | 8 +++++++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java b/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java index 4f861edd823..dc20a77d6d2 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java +++ b/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java @@ -34,7 +34,6 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.PayLifeCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.constants.CardType; @@ -58,14 +57,11 @@ public class VectisDominator extends CardImpl { this.subtype.add("Human"); this.subtype.add("Wizard"); - - this.power = new MageInt(0); this.toughness = new MageInt(2); - // {tap}: Tap target creature unless its controller pays 2 life. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new VectisDominatorEffect(new PayLifeCost(2)), new ManaCostsImpl("{1}")); - ability.addCost(new TapSourceCost()); + // {T}: Tap target creature unless its controller pays 2 life. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new VectisDominatorEffect(new PayLifeCost(2)), new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/SteadyProgress.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/SteadyProgress.java index 5da30c9d1c1..2061e03bc81 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/SteadyProgress.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/SteadyProgress.java @@ -29,6 +29,7 @@ package mage.sets.scarsofmirrodin; import java.util.UUID; +import mage.abilities.effects.Effect; import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DrawCardSourceControllerEffect; @@ -45,8 +46,13 @@ public class SteadyProgress extends CardImpl { super(ownerId, 45, "Steady Progress", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{U}"); this.expansionSetCode = "SOM"; + // Proliferate. (You choose any number of permanents and/or players with counters on them, then give each another counter of a kind already there.) this.getSpellAbility().addEffect(new ProliferateEffect()); - this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); + + // Draw a card. + Effect effect = new DrawCardSourceControllerEffect(1); + effect.setText("
Draw a card"); + this.getSpellAbility().addEffect(effect); } public SteadyProgress (final SteadyProgress card) { From 9cfadbda5b6477e69deccaf6e3abed0c643279c0 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 22:47:24 +0200 Subject: [PATCH 049/102] * Burning Earth - Fixed that the damage effect did not trigger. --- .../mage/sets/darksteel/BlinkmothNexus.java | 6 ++ .../src/mage/sets/magic2014/BurningEarth.java | 14 ++-- .../sets/scarsofmirrodin/RustedRelic.java | 2 +- .../abilities/keywords/MetalcraftTest.java | 69 ++++++++++++++++++ .../test/cards/triggers/BurningEarthTest.java | 71 +++++++++++++++++++ 5 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/BurningEarthTest.java diff --git a/Mage.Sets/src/mage/sets/darksteel/BlinkmothNexus.java b/Mage.Sets/src/mage/sets/darksteel/BlinkmothNexus.java index 42204e235e8..2ddadf0ed3a 100644 --- a/Mage.Sets/src/mage/sets/darksteel/BlinkmothNexus.java +++ b/Mage.Sets/src/mage/sets/darksteel/BlinkmothNexus.java @@ -63,8 +63,14 @@ public class BlinkmothNexus extends CardImpl { public BlinkmothNexus(UUID ownerId) { super(ownerId, 163, "Blinkmoth Nexus", Rarity.RARE, new CardType[]{CardType.LAND}, null); this.expansionSetCode = "DST"; + + // {T}: Add {1}to your mana pool. this.addAbility(new ColorlessManaAbility()); + + // {1}: Blinkmoth Nexus becomes a 1/1 Blinkmoth artifact creature with flying until end of turn. It's still a land. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new BlinkmothNexusToken(), "land", Duration.EndOfTurn), new GenericManaCost(1))); + + // {1}, {T}: Target Blinkmoth creature gets +1/+1 until end of turn. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(1, 1, Duration.EndOfTurn), new GenericManaCost(1)); ability.addCost(new TapSourceCost()); ability.addTarget(new TargetPermanent(filter)); diff --git a/Mage.Sets/src/mage/sets/magic2014/BurningEarth.java b/Mage.Sets/src/mage/sets/magic2014/BurningEarth.java index 7c62477e23a..9f4db5a56a4 100644 --- a/Mage.Sets/src/mage/sets/magic2014/BurningEarth.java +++ b/Mage.Sets/src/mage/sets/magic2014/BurningEarth.java @@ -34,7 +34,9 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.SetTargetPointer; -import mage.filter.common.FilterNonlandPermanent; +import mage.filter.common.FilterLandPermanent; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.SupertypePredicate; /** * @@ -42,16 +44,20 @@ import mage.filter.common.FilterNonlandPermanent; */ public class BurningEarth extends CardImpl { + private final static FilterLandPermanent filter = new FilterLandPermanent("a player taps a nonbasic land"); + + static { + filter.add(Predicates.not(new SupertypePredicate("Basic"))); + } + public BurningEarth(UUID ownerId) { super(ownerId, 130, "Burning Earth", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{R}"); this.expansionSetCode = "M14"; - // Whenever a player taps a nonbasic land for mana, Burning Earth deals 1 damage to that player. this.addAbility(new TapForManaAllTriggeredAbility( new DamageTargetEffect(1, true, "that player"), - new FilterNonlandPermanent("a player taps a nonbasic land"), - SetTargetPointer.PLAYER)); + filter, SetTargetPointer.PLAYER)); } public BurningEarth(final BurningEarth card) { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/RustedRelic.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/RustedRelic.java index e6642c03feb..91a9e093543 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/RustedRelic.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/RustedRelic.java @@ -55,7 +55,7 @@ public class RustedRelic extends CardImpl { new ConditionalContinuousEffect( new BecomesCreatureSourceEffect(new RustedRelicToken(), "artifact", Duration.WhileOnBattlefield), MetalcraftCondition.getInstance(), - "Metalcraft - Rusted Relic is a 5/5 Golem artifact creature as long as you control three or more artifacts"))); + "Metalcraft - {this} is a 5/5 Golem artifact creature as long as you control three or more artifacts"))); } public RustedRelic (final RustedRelic card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java new file mode 100644 index 00000000000..42a390fefcf --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MetalcraftTest.java @@ -0,0 +1,69 @@ +/* + * 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.cards.abilities.keywords; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class MetalcraftTest extends CardTestPlayerBase { + + /** + * Rusted Relic or Blinkmoth nexus is bugged + * Either Relic does not see Blinkmoth as an artifact or it does not turn + * into one when it should. + * + */ + @Test + public void testMetalcraftFromBlinkmoth() { + addCard(Zone.BATTLEFIELD, playerA, "Darksteel Citadel",1); + + // Metalcraft - {this} is a 5/5 Golem artifact creature as long as you control three or more artifacts + addCard(Zone.BATTLEFIELD, playerA, "Rusted Relic", 1); + + // {T}: Add {1}to your mana pool. + // {1}: Blinkmoth Nexus becomes a 1/1 Blinkmoth artifact creature with flying until end of turn. It's still a land. + // {1}, {T}: Target Blinkmoth creature gets +1/+1 until end of turn. + addCard(Zone.BATTLEFIELD, playerA, "Blinkmoth Nexus", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}: Until end of turn {this} becomes "); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPowerToughness(playerA, "Blinkmoth Nexus", 1, 1); + assertPowerToughness(playerA, "Rusted Relic", 5, 5); + + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BurningEarthTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BurningEarthTest.java new file mode 100644 index 00000000000..21e6af173df --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/BurningEarthTest.java @@ -0,0 +1,71 @@ +/* + * 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.cards.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class BurningEarthTest extends CardTestPlayerBase { + + /** + * Burning Earth - It doesn't cause the damage it should. My opponent taps a + * Blood Crypt and an Overgrown Tomb for black and green mana respectively + * and casts his card all the while without taking any damage. + * + */ + @Test + public void testBurningEarth() { + // Destroy target artifact or creature. It can't be regenerated. + addCard(Zone.HAND, playerB, "Putrefy"); // {1}{B}{G} + addCard(Zone.BATTLEFIELD, playerB, "Darksteel Citadel", 1); + addCard(Zone.BATTLEFIELD, playerB, "Blood Crypt", 1); // {B}{R} + addCard(Zone.BATTLEFIELD, playerB, "Overgrown Tomb", 1); // {B}{G} + + // Whenever a player taps a nonbasic land for mana, Burning Earth deals 1 damage to that player. + addCard(Zone.BATTLEFIELD, playerA, "Burning Earth", 1); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Putrefy", "Silvercoat Lion"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, "Putrefy", 1); + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + + assertLife(playerA, 20); + assertLife(playerB, 17); + } + +} From 5b57a5b6ee97cdf7a818cc88026e019f080920ae Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 22:54:11 +0200 Subject: [PATCH 050/102] * Dash - Fixed missing tooltip text for triggered return to hand ability. --- Mage/src/mage/abilities/keyword/DashAbility.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage/src/mage/abilities/keyword/DashAbility.java b/Mage/src/mage/abilities/keyword/DashAbility.java index dbb7ddbcbe4..a80b5f50796 100644 --- a/Mage/src/mage/abilities/keyword/DashAbility.java +++ b/Mage/src/mage/abilities/keyword/DashAbility.java @@ -77,6 +77,7 @@ public class DashAbility extends StaticAbility implements AlternativeSourceCosts new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.Custom, false), DashedCondition.getInstance(), false, "",""); Effect effect = new ReturnToHandTargetEffect(); + effect.setText("return the dashed creature from the battlefield to its owner's hand"); effect.setTargetPointer(new FixedTarget(card.getId())); ability.addEffect(new CreateDelayedTriggeredAbilityEffect(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect), false)); addSubAbility(ability); From b58d2e88ea3043fec7d9f40d4b4f7a822873b097 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 3 Jun 2015 23:49:54 +0200 Subject: [PATCH 051/102] * Novijen Sages - Fixed that it was not possible to pay the activated ability with the counters of Novijen Sages. --- .../java/mage/player/ai/ComputerPlayer.java | 7 ++ .../src/mage/player/human/HumanPlayer.java | 4 +- .../mage/sets/timespiral/MightOfOldKrosa.java | 1 - .../additional/RemoveCounterCostTest.java | 65 +++++++++++++++++++ .../costs/common/RemoveCounterCost.java | 2 +- 5 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/RemoveCounterCostTest.java diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 2208d111554..05858f63361 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -288,6 +288,13 @@ public class ComputerPlayer extends PlayerImpl implements Player { } else { targets = threats(opponentId, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); } + if (targets.isEmpty() && target.isRequired()) { + if (!outcome.isGood()) { + targets = threats(abilityControllerId, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); + } else { + targets = threats(opponentId, sourceId, ((TargetPermanent) target).getFilter(), game, target.getTargets()); + } + } } for (Permanent permanent : targets) { if (((TargetPermanent) target).canTarget(abilityControllerId, permanent.getId(), null, game) && !target.getTargets().contains(permanent.getId())) { diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 9faf9554cea..767819d066b 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -244,8 +244,8 @@ public class HumanPlayer extends PlayerImpl { } while (!abort) { Set targetIds = target.possibleTargets(sourceId, abilityControllerId, game); - if (targetIds == null || targetIds.isEmpty()) { - return false; + if (targetIds == null || targetIds.isEmpty()) { + return target.getTargets().size() >= target.getNumberOfTargets(); } boolean required = target.isRequired(sourceId, game); if (target.getTargets().size() >= target.getNumberOfTargets()) { diff --git a/Mage.Sets/src/mage/sets/timespiral/MightOfOldKrosa.java b/Mage.Sets/src/mage/sets/timespiral/MightOfOldKrosa.java index eb4fac5aba9..cf4d66310b8 100644 --- a/Mage.Sets/src/mage/sets/timespiral/MightOfOldKrosa.java +++ b/Mage.Sets/src/mage/sets/timespiral/MightOfOldKrosa.java @@ -48,7 +48,6 @@ public class MightOfOldKrosa extends CardImpl { super(ownerId, 204, "Might of Old Krosa", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{G}"); this.expansionSetCode = "TSP"; - // Target creature gets +2/+2 until end of turn. If you cast this spell during your main phase, that creature gets +4/+4 until end of turn instead. this.getSpellAbility().addEffect(new ConditionalContinuousEffect( new BoostTargetEffect(4,4, Duration.EndOfTurn), diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/RemoveCounterCostTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/RemoveCounterCostTest.java new file mode 100644 index 00000000000..8a271fad85e --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/cost/additional/RemoveCounterCostTest.java @@ -0,0 +1,65 @@ +/* + * 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.cards.cost.additional; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class RemoveCounterCostTest extends CardTestPlayerBase { + + @Test + public void testNovijenSages() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 7); + // Graft 4 + // {1}, Remove two +1/+1 counters from among creatures you control: Draw a card. + addCard(Zone.HAND, playerA, "Novijen Sages"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Novijen Sages"); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1},Remove two +1/+1 counters"); + setChoice(playerA, "X=2"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Novijen Sages", 1); + assertPowerToughness(playerA, "Novijen Sages", 2, 2); + + assertHandCount(playerA, 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + } + +} diff --git a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java index 589f477d89b..28526853e3b 100644 --- a/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java +++ b/Mage/src/mage/abilities/costs/common/RemoveCounterCost.java @@ -123,7 +123,7 @@ public class RemoveCounterCost extends CostImpl { int numberOfCountersSelected = 1; if (countersLeft > 1 && countersOnPermanent > 1) { numberOfCountersSelected = controller.getAmount(1, Math.min(countersLeft, countersOnPermanent), - new StringBuilder("Remove how many counters from ").append(permanent.getLogName()).toString(), game); + new StringBuilder("Remove how many counters from ").append(permanent.getIdName()).toString(), game); } permanent.removeCounters(counterName, numberOfCountersSelected, game); if (permanent.getCounters().getCount(counterName) == 0 ){ From b4387456c9c220019edca52cb1b08b98be187a38 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Thu, 4 Jun 2015 00:15:23 -0400 Subject: [PATCH 052/102] Exiling all graveyards abstracted out into common effect. Added Morningtide. Implementation of Rest In Peace and Relic of Progenitus abstracted out. --- .../sets/returntoravnica/RestInPeace.java | 43 +---------- .../sets/shardsofalara/RelicOfProgenitus.java | 42 +---------- .../src/mage/sets/torment/Morningtide.java | 58 +++++++++++++++ .../ExileGraveyardAllPlayersEffect.java | 74 +++++++++++++++++++ 4 files changed, 136 insertions(+), 81 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/torment/Morningtide.java create mode 100644 Mage/src/mage/abilities/effects/common/ExileGraveyardAllPlayersEffect.java diff --git a/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java b/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java index 0462939b00b..e2adf646e2d 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/RestInPeace.java @@ -31,8 +31,8 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -72,9 +72,8 @@ public class RestInPeace extends CardImpl { super(ownerId, 18, "Rest in Peace", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); this.expansionSetCode = "RTR"; - // When Rest in Peace enters the battlefield, exile all cards from all graveyards. - this.addAbility(new EntersBattlefieldTriggeredAbility(new RestInPeaceExileAllEffect())); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ExileGraveyardAllPlayersEffect())); // If a card or token would be put into a graveyard from anywhere, exile it instead. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new RestInPeaceReplacementEffect())); @@ -90,43 +89,6 @@ public class RestInPeace extends CardImpl { } } -class RestInPeaceExileAllEffect extends OneShotEffect { - - public RestInPeaceExileAllEffect() { - super(Outcome.Detriment); - staticText = "exile all cards from all graveyards"; - } - - public RestInPeaceExileAllEffect(final RestInPeaceExileAllEffect effect) { - super(effect); - } - - @Override - public RestInPeaceExileAllEffect copy() { - return new RestInPeaceExileAllEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - for (UUID playerId : controller.getInRange()) { - Player player = game.getPlayer(playerId); - if (player != null) { - for (UUID cid : player.getGraveyard().copy()) { - Card card = game.getCard(cid); - if (card != null) { - controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.GRAVEYARD, true); - } - } - } - } - return true; - } - return false; - } -} - class RestInPeaceReplacementEffect extends ReplacementEffectImpl { public RestInPeaceReplacementEffect() { @@ -173,5 +135,4 @@ class RestInPeaceReplacementEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { return ((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD; } - } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/RelicOfProgenitus.java b/Mage.Sets/src/mage/sets/shardsofalara/RelicOfProgenitus.java index 49e54fd61bc..2b967b40bac 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/RelicOfProgenitus.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/RelicOfProgenitus.java @@ -35,6 +35,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -63,11 +64,10 @@ public class RelicOfProgenitus extends CardImpl { firstAbility.addTarget(new TargetPlayer()); this.addAbility(firstAbility); // {1}, Exile Relic of Progenitus: Exile all cards from all graveyards. Draw a card. - Ability secondAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RelicOfProgenitusEffect2(),new GenericManaCost(1)); + Ability secondAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileGraveyardAllPlayersEffect(),new GenericManaCost(1)); secondAbility.addCost(new ExileSourceCost()); secondAbility.addEffect(new DrawCardSourceControllerEffect(1)); this.addAbility(secondAbility); - } public RelicOfProgenitus(final RelicOfProgenitus card) { @@ -114,41 +114,3 @@ class RelicOfProgenitusEffect extends OneShotEffect { return false; } } - -class RelicOfProgenitusEffect2 extends OneShotEffect { - - public RelicOfProgenitusEffect2() { - super(Outcome.Detriment); - staticText = "Exile all cards from all graveyards"; - } - - public RelicOfProgenitusEffect2(final RelicOfProgenitusEffect2 effect) { - super(effect); - } - - @Override - public RelicOfProgenitusEffect2 copy() { - return new RelicOfProgenitusEffect2(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - - for (UUID playerId : controller.getInRange()) { - Player player = game.getPlayer(playerId); - if (player != null) { - for (UUID cid : player.getGraveyard().copy()) { - Card card = game.getCard(cid); - if (card != null) { - controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.GRAVEYARD, true); - } - } - } - } - return true; - } -} diff --git a/Mage.Sets/src/mage/sets/torment/Morningtide.java b/Mage.Sets/src/mage/sets/torment/Morningtide.java new file mode 100644 index 00000000000..cc699128a42 --- /dev/null +++ b/Mage.Sets/src/mage/sets/torment/Morningtide.java @@ -0,0 +1,58 @@ +/* + * 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 mage.sets.torment; + +import java.util.UUID; +import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class Morningtide extends CardImpl { + + public Morningtide(UUID ownerId) { + super(ownerId, 10, "Morningtide", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{W}"); + this.expansionSetCode = "TOR"; + + // Exile all cards from all graveyards. + this.getSpellAbility().addEffect(new ExileGraveyardAllPlayersEffect()); + } + + public Morningtide(final Morningtide card) { + super(card); + } + + @Override + public Morningtide copy() { + return new Morningtide(this); + } +} \ No newline at end of file diff --git a/Mage/src/mage/abilities/effects/common/ExileGraveyardAllPlayersEffect.java b/Mage/src/mage/abilities/effects/common/ExileGraveyardAllPlayersEffect.java new file mode 100644 index 00000000000..d5b8e67c35a --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/ExileGraveyardAllPlayersEffect.java @@ -0,0 +1,74 @@ +/* + * 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 mage.abilities.effects.common; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.Card; +import mage.constants.Outcome; +import mage.constants.Zone; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author Jgod + */ +public class ExileGraveyardAllPlayersEffect extends OneShotEffect { + public ExileGraveyardAllPlayersEffect() { + super(Outcome.Detriment); + staticText = "exile all cards from all graveyards"; + } + + @Override + public ExileGraveyardAllPlayersEffect copy() { + return new ExileGraveyardAllPlayersEffect(); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; + } + + for (UUID playerId : controller.getInRange()) { + Player player = game.getPlayer(playerId); + if (player != null) { + for (UUID cid : player.getGraveyard().copy()) { + Card card = game.getCard(cid); + if (card != null) { + controller.moveCardToExileWithInfo(card, null, "", source.getSourceId(), game, Zone.GRAVEYARD, true); + } + } + } + } + return true; + } +} \ No newline at end of file From 53396a44f235868661813d83d5f995a4d3553a44 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 4 Jun 2015 13:50:39 +0200 Subject: [PATCH 053/102] Added some test and some minor fixes to effect ability handling. --- .../java/mage/player/ai/ComputerPlayer.java | 8 +- .../mage/sets/magic2013/PublicExecution.java | 1 - Mage.Sets/src/mage/sets/mirrodin/Shatter.java | 1 + .../mage/sets/tenthedition/DemonsHorn.java | 54 ++------ .../cards/continuous/MightOfOldKrosaTest.java | 83 +++++++++++ .../test/cards/copy/PhantasmalImageTest.java | 129 ++++++++++++++++-- .../triggers/state/SynodCenturionTest.java | 4 + .../mage/abilities/StateTriggeredAbility.java | 21 ++- .../common/BecomesTargetTriggeredAbility.java | 3 +- .../effects/common/SacrificeSourceEffect.java | 9 +- .../abilities/keyword/PersistAbility.java | 19 ++- 11 files changed, 256 insertions(+), 76 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/continuous/MightOfOldKrosaTest.java diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 05858f63361..170b26a3ce3 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -972,9 +972,11 @@ public class ComputerPlayer extends PlayerImpl implements Player { @Override public boolean activateAbility(ActivatedAbility ability, Game game) { - for (Target target: ability.getModes().getMode().getTargets()) { - for (UUID targetId: target.getTargets()) { - game.fireEvent(GameEvent.getEvent(EventType.TARGETED, targetId, ability.getId(), ability.getControllerId())); + if (!isTestMode()) { // Test player already sends target event as he selects the target + for (Target target: ability.getModes().getMode().getTargets()) { + for (UUID targetId: target.getTargets()) { + game.fireEvent(GameEvent.getEvent(EventType.TARGETED, targetId, ability.getId(), ability.getControllerId())); + } } } return super.activateAbility(ability, game); diff --git a/Mage.Sets/src/mage/sets/magic2013/PublicExecution.java b/Mage.Sets/src/mage/sets/magic2013/PublicExecution.java index aa8db6a0cd9..75758f1f41b 100644 --- a/Mage.Sets/src/mage/sets/magic2013/PublicExecution.java +++ b/Mage.Sets/src/mage/sets/magic2013/PublicExecution.java @@ -61,7 +61,6 @@ public class PublicExecution extends CardImpl { super(ownerId, 105, "Public Execution", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{5}{B}"); this.expansionSetCode = "M13"; - // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); diff --git a/Mage.Sets/src/mage/sets/mirrodin/Shatter.java b/Mage.Sets/src/mage/sets/mirrodin/Shatter.java index 6ee15fcc26d..44bce28e959 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/Shatter.java +++ b/Mage.Sets/src/mage/sets/mirrodin/Shatter.java @@ -52,6 +52,7 @@ public class Shatter extends CardImpl { super(ownerId, 105, "Shatter", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}"); this.expansionSetCode = "MRD"; + // Destroy target artifact. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetPermanent(filter)); } diff --git a/Mage.Sets/src/mage/sets/tenthedition/DemonsHorn.java b/Mage.Sets/src/mage/sets/tenthedition/DemonsHorn.java index acea56e7df1..14a17bd3172 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/DemonsHorn.java +++ b/Mage.Sets/src/mage/sets/tenthedition/DemonsHorn.java @@ -29,16 +29,15 @@ package mage.sets.tenthedition; import java.util.UUID; +import mage.ObjectColor; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.Zone; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SpellCastAllTriggeredAbility; +import mage.abilities.dynamicvalue.common.StaticValue; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.stack.Spell; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.ColorPredicate; /** * @@ -46,10 +45,18 @@ import mage.game.stack.Spell; */ public class DemonsHorn extends CardImpl { + private final static FilterSpell filter = new FilterSpell("a black spell"); + + static { + filter.add(new ColorPredicate(ObjectColor.BLACK)); + } + public DemonsHorn(UUID ownerId) { super(ownerId, 320, "Demon's Horn", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{2}"); this.expansionSetCode = "10E"; - this.addAbility(new DemonsHornAbility()); + + // Whenever a player casts a black spell, you may gain 1 life. + this.addAbility(new SpellCastAllTriggeredAbility(new GainLifeEffect(new StaticValue(1), "you may gain 1 life"), filter, true)); } public DemonsHorn(final DemonsHorn card) { @@ -62,36 +69,3 @@ public class DemonsHorn extends CardImpl { } } - -class DemonsHornAbility extends TriggeredAbilityImpl { - - public DemonsHornAbility() { - super(Zone.BATTLEFIELD, new GainLifeEffect(1), true); - } - - public DemonsHornAbility(final DemonsHornAbility ability) { - super(ability); - } - - @Override - public DemonsHornAbility copy() { - return new DemonsHornAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == EventType.SPELL_CAST) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isBlack()) { - return true; - } - } - return false; - } - - @Override - public String getRule() { - return "Whenever a player casts a black spell, you may gain 1 life."; - } - -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MightOfOldKrosaTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MightOfOldKrosaTest.java new file mode 100644 index 00000000000..17db1377bd5 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/MightOfOldKrosaTest.java @@ -0,0 +1,83 @@ +/* + * 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.cards.continuous; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class MightOfOldKrosaTest extends CardTestPlayerBase { + + @Test + public void testTwiceMightOfOldKrosaBeginCombat() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.HAND, playerA, "Might of Old Krosa", 2); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Might of Old Krosa", "Silvercoat Lion"); + castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Might of Old Krosa", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Might of Old Krosa", 2); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 6, 6); + } + /** + * Threw two Might of old Krosa's onto a creature, but only one had any effect. + */ + + @Test + public void testTwiceMightOfOldKrosa() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.HAND, playerA, "Might of Old Krosa", 2); + + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Might of Old Krosa", "Silvercoat Lion"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Might of Old Krosa", "Silvercoat Lion"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerA, "Might of Old Krosa", 2); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + assertPowerToughness(playerA, "Silvercoat Lion", 10, 10); + } + +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java index 2d805bbd081..deb1e8b99f9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java @@ -316,7 +316,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Frost Titan"); addCard(Zone.HAND, playerA, "Terror"); // {1}{U} - Target creature gains shroud until end of turn and can't be blocked this turn. - addCard(Zone.HAND, playerA, "Veil of Secrecy"); + addCard(Zone.HAND, playerA, "Veil of Secrecy"); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); addCard(Zone.BATTLEFIELD, playerA, "Island", 2); @@ -328,32 +328,31 @@ public class PhantasmalImageTest extends CardTestPlayerBase { setChoice(playerB, "Frost Titan"); castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Terror", "Frost Titan"); // of player Bs Phantasmal Image copying Frost Titan - // should be countered if not paying {2} + // should be countered if not paying {2} setStopAt(2, PhaseStep.END_TURN); execute(); assertGraveyardCount(playerA, "Veil of Secrecy", 1); assertGraveyardCount(playerA, "Terror", 1); - + assertLife(playerB, 20); assertLife(playerA, 20); - assertPermanentCount(playerA, "Frost Titan", 1); - + assertPermanentCount(playerA, "Frost Titan", 1); + assertGraveyardCount(playerB, "Phantasmal Image", 1); // if triggered ability did not work, the Titan would be in the graveyard instaed } - + // I've casted a Phantasmal Image targeting opponent's Wurmcoil Engine // When my Phantasmal Image died, it didn't triggered the Wurmcoil Engine's last ability // (When Wurmcoil Engine dies, put a 3/3 colorless Wurm artifact creature token with deathtouch and // a 3/3 colorless Wurm artifact creature token with lifelink onto the battlefield.) - @Test public void testDiesTriggeredAbilities() { addCard(Zone.BATTLEFIELD, playerA, "Wurmcoil Engine"); - // Destroy target artifact or enchantment. + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. addCard(Zone.HAND, playerA, "Public Execution"); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); @@ -364,22 +363,124 @@ public class PhantasmalImageTest extends CardTestPlayerBase { setChoice(playerB, "Wurmcoil Engine"); castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Wurmcoil Engine"); // of player Bs Phantasmal Image copying Frost Titan - // should be countered if not paying {2} + // should be countered if not paying {2} setStopAt(2, PhaseStep.END_TURN); execute(); assertGraveyardCount(playerA, "Public Execution", 1); - + assertLife(playerB, 20); assertLife(playerA, 20); - - assertPermanentCount(playerA, "Wurmcoil Engine", 1); - + assertPermanentCount(playerA, "Wurmcoil Engine", 1); + assertGraveyardCount(playerB, "Phantasmal Image", 1); assertPermanentCount(playerB, "Wurm", 2); // if triggered ability did not work, the Titan would be in the graveyard instaed } - + + /** + * Phantasmal Image is not regestering Leave the battlefield triggers, + * persist and undying triggers + */ + @Test + public void testLeavesTheBattlefieldTriggeredAbilities() { + // Shadow (This creature can block or be blocked by only creatures with shadow.) + // When Thalakos Seer leaves the battlefield, draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Thalakos Seer"); + + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. + addCard(Zone.HAND, playerA, "Public Execution"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + addCard(Zone.HAND, playerB, "Phantasmal Image"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted + setChoice(playerB, "Thalakos Seer"); + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Thalakos Seer"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Public Execution", 1); + + assertLife(playerB, 20); + assertLife(playerA, 20); + + assertPermanentCount(playerA, "Thalakos Seer", 1); + + assertGraveyardCount(playerB, "Phantasmal Image", 1); + + assertHandCount(playerB, 2); // 1 from draw turn 2 and 1 from Thalakos Seer leaves the battlefield trigger + } + + /** + * Action + * Game State 1 -----------------> Game State 2 + * (On 'field) (Move to GY) (In graveyard) + * + * LTB abilities such as Persist are expceptional in that they trigger based on their existence and + * state of objects before the event (Game State 1, when the card is on the battlefield) rather than + * after (Game State 2, when the card is in the graveyard). It doesn't matter that the LTB ability + * doesn't exist in Game State 2. [CR 603.6d] + * + * 603.6d Normally, objects that exist immediately after an event are checked to see if the event matched any trigger conditions. + * Continuous effects that exist at that time are used to determine what the trigger conditions are and what the objects involved + * in the event look like. However, some triggered abilities must be treated specially. Leaves-the-battlefield abilities, abilities + * that trigger when a permanent phases out, abilities that trigger when an object that all players can see is put into a hand or + * library, abilities that trigger specifically when an object becomes unattached, abilities that trigger when a player loses control + * of an object, and abilities that trigger when a player planeswalks away from a plane will trigger based on their existence, and + * the appearance of objects, prior to the event rather than afterward. The game has to “look back in time” to determine if these abilities trigger. + * + * Example: Two creatures are on the battlefield along with an artifact that has the ability “Whenever a creature dies, you gain 1 life.” + * Someone plays a spell that destroys all artifacts, creatures, and enchantments. The artifact’s ability triggers twice, even though + * the artifact goes to its owner’s graveyard at the same time as the creatures. + * + */ + @Test + public void testPersist() { + addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); + // When Kitchen Finks enters the battlefield, you gain 2 life. + // Persist (When this creature dies, if it had no -1/-1 counters on it, return it to the battlefield under its owner's control with a -1/-1 counter on it.) + addCard(Zone.HAND, playerA, "Kitchen Finks"); + + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. + addCard(Zone.HAND, playerA, "Public Execution"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + + // You may have Phantasmal Image enter the battlefield as a copy of any creature + // on the battlefield, except it's an Illusion in addition to its other types and + // it gains "When this creature becomes the target of a spell or ability, sacrifice it." + addCard(Zone.HAND, playerB, "Phantasmal Image"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitchen Finks"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted + setChoice(playerB, "Kitchen Finks"); + + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Kitchen Finks"); + setChoice(playerB, "Kitchen Finks"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Public Execution", 1); + + assertLife(playerA, 22); + assertLife(playerB, 24); + + assertPermanentCount(playerA, "Kitchen Finks", 1); + + assertHandCount(playerB, "Phantasmal Image", 0); + assertGraveyardCount(playerB, "Phantasmal Image", 0); + assertPermanentCount(playerB, "Kitchen Finks", 1); + assertPowerToughness(playerB, "Kitchen Finks", 2, 1); + + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java index 27043288f72..03ecd3287aa 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/state/SynodCenturionTest.java @@ -25,11 +25,15 @@ public class SynodCenturionTest extends CardTestPlayerBase { @Test public void testAlone() { addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); + // Whenever a player casts a black spell, you may gain 1 life. addCard(Zone.BATTLEFIELD, playerA, "Demon's Horn"); + // Destroy target artifact. addCard(Zone.HAND, playerA, "Shatter"); + // When you control no other artifacts, sacrifice Synod Centurion. addCard(Zone.HAND, playerA, "Synod Centurion"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Synod Centurion"); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Shatter", "Demon's Horn"); setStopAt(1, PhaseStep.END_TURN); diff --git a/Mage/src/mage/abilities/StateTriggeredAbility.java b/Mage/src/mage/abilities/StateTriggeredAbility.java index 6b0d9e6f613..1c8cfadfb37 100644 --- a/Mage/src/mage/abilities/StateTriggeredAbility.java +++ b/Mage/src/mage/abilities/StateTriggeredAbility.java @@ -32,6 +32,7 @@ import java.util.UUID; import mage.abilities.effects.Effect; import mage.constants.Zone; import mage.game.Game; +import mage.game.events.GameEvent; /** * @@ -48,23 +49,29 @@ public abstract class StateTriggeredAbility extends TriggeredAbilityImpl { } @Override - public void trigger(Game game, UUID controllerId) { + public final boolean checkEventType(GameEvent event, Game game) { //20100716 - 603.8 - Boolean triggered = (Boolean) game.getState().getValue(this.getSourceId().toString() + "triggered"); + Boolean triggered = (Boolean) game.getState().getValue(getSourceId().toString() + "triggered"); if (triggered == null) { triggered = Boolean.FALSE; } - if (!triggered) { - game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); - super.trigger(game, controllerId); - } + return !triggered; + } + + + @Override + public void trigger(Game game, UUID controllerId) { + //20100716 - 603.8 + game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.TRUE); + super.trigger(game, controllerId); } @Override public boolean resolve(Game game) { //20100716 - 603.8 + boolean result = super.resolve(game); game.getState().setValue(this.getSourceId().toString() + "triggered", Boolean.FALSE); - return super.resolve(game); + return result; } public void counter(Game game) { diff --git a/Mage/src/mage/abilities/common/BecomesTargetTriggeredAbility.java b/Mage/src/mage/abilities/common/BecomesTargetTriggeredAbility.java index 12e9e3db26c..c80efba2104 100644 --- a/Mage/src/mage/abilities/common/BecomesTargetTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/BecomesTargetTriggeredAbility.java @@ -32,7 +32,6 @@ import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.Effect; import mage.game.Game; import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; /** * @@ -60,7 +59,7 @@ public class BecomesTargetTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - return event.getTargetId().equals(sourceId); + return event.getTargetId().equals(getSourceId()); } @Override diff --git a/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java index 0b78c64aeed..6d8727059a9 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeSourceEffect.java @@ -28,6 +28,7 @@ package mage.abilities.effects.common; +import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; @@ -56,13 +57,17 @@ public class SacrificeSourceEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { + MageObject sourceObject = source.getSourceObjectIfItStillExists(game); + if (sourceObject instanceof Permanent) { + Permanent permanent = (Permanent) sourceObject; // you can only sacrifice a permanent you control if (source.getControllerId().equals(permanent.getControllerId())) { return permanent.sacrifice(source.getSourceId(), game); } return true; + } else { + // no permanent? + sourceObject.getName(); } return false; } diff --git a/Mage/src/mage/abilities/keyword/PersistAbility.java b/Mage/src/mage/abilities/keyword/PersistAbility.java index 172808456a6..2577fec20eb 100644 --- a/Mage/src/mage/abilities/keyword/PersistAbility.java +++ b/Mage/src/mage/abilities/keyword/PersistAbility.java @@ -35,10 +35,11 @@ import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.constants.Duration; import mage.constants.Outcome; -import mage.constants.Zone; import mage.counters.CounterType; import mage.game.Game; +import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.GameEvent; +import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; import mage.target.targetpointer.FixedTarget; @@ -68,9 +69,11 @@ public class PersistAbility extends DiesTriggeredAbility { @Override public boolean checkTrigger(GameEvent event, Game game) { if (super.checkTrigger(event, game)) { - Permanent p = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (p.getCounters().getCount(CounterType.M1M1) == 0) { - game.getState().setValue("persist" + getSourceId().toString(), new FixedTarget(p.getId())); + Permanent permanent = ((ZoneChangeEvent) event).getTarget(); + if (permanent.getCounters().getCount(CounterType.M1M1) == 0) { + FixedTarget fixedTarget = new FixedTarget(permanent.getId()); + fixedTarget.init(game, this); + game.getState().setValue("persist" + getSourceId().toString(), fixedTarget); return true; } } @@ -109,7 +112,7 @@ class PersistEffect extends OneShotEffect { class PersistReplacementEffect extends ReplacementEffectImpl { PersistReplacementEffect() { - super(Duration.OneUse, Outcome.UnboostCreature, false); + super(Duration.Custom, Outcome.UnboostCreature, false); selfScope = true; staticText = "return it to the battlefield under its owner's control with a -1/-1 counter on it"; } @@ -129,7 +132,7 @@ class PersistReplacementEffect extends ReplacementEffectImpl { if (permanent != null) { permanent.addCounters(CounterType.M1M1.createInstance(), game); } - used = true; + discard(); return false; } @@ -142,7 +145,9 @@ class PersistReplacementEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getTargetId().equals(source.getSourceId())) { Object fixedTarget = game.getState().getValue("persist" + source.getSourceId().toString()); - if (fixedTarget instanceof FixedTarget && ((FixedTarget) fixedTarget).getFirst(game, source).equals(source.getSourceId())) { + if (fixedTarget instanceof FixedTarget && ((FixedTarget) fixedTarget).getTarget().equals(source.getSourceId()) && + ((FixedTarget) fixedTarget).getZoneChangeCounter() + 1 == game.getState().getZoneChangeCounter(source.getSourceId())) { + return true; } } From 59ae174d2be81e7cdbfcf0159a0e27966f4a633f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 4 Jun 2015 13:58:20 +0200 Subject: [PATCH 054/102] Added a test. --- .../sets/avacynrestored/ButcherGhoul.java | 1 + .../test/cards/copy/PhantasmalImageTest.java | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/Mage.Sets/src/mage/sets/avacynrestored/ButcherGhoul.java b/Mage.Sets/src/mage/sets/avacynrestored/ButcherGhoul.java index cf186b902ab..4c705d5594b 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/ButcherGhoul.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/ButcherGhoul.java @@ -48,6 +48,7 @@ public class ButcherGhoul extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); + // Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.) this.addAbility(new UndyingAbility()); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java index deb1e8b99f9..7cd0d98e3b0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java @@ -483,4 +483,47 @@ public class PhantasmalImageTest extends CardTestPlayerBase { assertPowerToughness(playerB, "Kitchen Finks", 2, 1); } + + @Test + public void testUndying() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + // Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.) + addCard(Zone.HAND, playerA, "Butcher Ghoul"); + + // Destroy target creature an opponent controls. Each other creature that player controls gets -2/-0 until end of turn. + addCard(Zone.HAND, playerA, "Public Execution"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); + + addCard(Zone.BATTLEFIELD, playerB, "Island", 2); + + // You may have Phantasmal Image enter the battlefield as a copy of any creature + // on the battlefield, except it's an Illusion in addition to its other types and + // it gains "When this creature becomes the target of a spell or ability, sacrifice it." + addCard(Zone.HAND, playerB, "Phantasmal Image"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Butcher Ghoul"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted + setChoice(playerB, "Butcher Ghoul"); + + + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Butcher Ghoul"); + setChoice(playerB, "Butcher Ghoul"); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Public Execution", 1); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerA, "Butcher Ghoul", 1); + + assertHandCount(playerB, "Phantasmal Image", 0); + assertGraveyardCount(playerB, "Phantasmal Image", 0); + assertPermanentCount(playerB, "Butcher Ghoul", 1); + assertPowerToughness(playerB, "Butcher Ghoul", 2, 2); + + } } From b583f616bfdb4a78ae0190eadd0fa4119abfc25d Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 4 Jun 2015 14:12:52 +0200 Subject: [PATCH 055/102] * Burrenton Forge-Tende - Fixed that every source could be selected.. --- .../effects/common/PreventDamageBySourceEffect.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java b/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java index da4b7082b3c..9845cd5824e 100644 --- a/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java @@ -54,11 +54,10 @@ public class PreventDamageBySourceEffect extends PreventionEffectImpl { public PreventDamageBySourceEffect(FilterObject filterObject) { super(Duration.EndOfTurn); - if (filterObject.getMessage().equals("a")) { - this.target = new TargetSource(new FilterObject("source")); - } else { - this.target = new TargetSource(new FilterObject(filterObject.getMessage() + " source")); + if (!filterObject.getMessage().endsWith("source")) { + filterObject.setMessage(filterObject.getMessage() + " source"); } + this.target = new TargetSource(filterObject); staticText = "Prevent all damage " + filterObject.getMessage() + " source of your choice would deal this turn"; } From ca9ee416e83253c20d05e851aebda55318dfcfe7 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 4 Jun 2015 14:48:06 +0200 Subject: [PATCH 056/102] Added test for Mana Flare. --- .../src/mage/sets/ravnica/WateryGrave.java | 1 + .../mage/test/cards/mana/ManaFlareTest.java | 90 +++++++++++++++++++ .../AddManaOfAnyTypeProducedEffect.java | 2 +- 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaFlareTest.java diff --git a/Mage.Sets/src/mage/sets/ravnica/WateryGrave.java b/Mage.Sets/src/mage/sets/ravnica/WateryGrave.java index 526a60a6535..df912b539d6 100644 --- a/Mage.Sets/src/mage/sets/ravnica/WateryGrave.java +++ b/Mage.Sets/src/mage/sets/ravnica/WateryGrave.java @@ -49,6 +49,7 @@ public class WateryGrave extends CardImpl { this.expansionSetCode = "RAV"; this.subtype.add("Island"); this.subtype.add("Swamp"); + this.addAbility(new BlueManaAbility()); this.addAbility(new BlackManaAbility()); this.addAbility(new AsEntersBattlefieldAbility(new TapSourceUnlessPaysEffect(new PayLifeCost(2)), "you may pay 2 life. If you don't, {this} enters the battlefield tapped")); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaFlareTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaFlareTest.java new file mode 100644 index 00000000000..823b579cce4 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaFlareTest.java @@ -0,0 +1,90 @@ +/* + * 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.cards.mana; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class ManaFlareTest extends CardTestPlayerBase { + + @Test + public void testIsland() { + // {T}: Add one mana of any color to your mana pool. Spend this mana only to cast a multicolored spell. + addCard(Zone.BATTLEFIELD, playerA, "Mana Flare", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 1); + + // Creature {U}{U} + // {U},{T} :Return target permanent you control to its owner's hand. + addCard(Zone.HAND, playerA, "Vedalken Mastermind", 1); + + // because available mana calculation does not work correctly with Mana Flare we have to tap the land manually + activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U} to your mana pool"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vedalken Mastermind"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Vedalken Mastermind", 1); + + } + + + /** + * Mana Flare is only adding colorless mana, at least off of dual lands (Watery Grave in this instance). + * Island only adds colorless. Plains adds white though. + */ + @Test + public void testWateryGrave() { + // {T}: Add one mana of any color to your mana pool. Spend this mana only to cast a multicolored spell. + addCard(Zone.BATTLEFIELD, playerB, "Mana Flare", 1); + addCard(Zone.BATTLEFIELD, playerB, "Watery Grave", 1); + + // Creature {B}{B} + // {B}: Nantuko Shade gets +1/+1 until end of turn. + addCard(Zone.HAND, playerB, "Nantuko Shade", 1); + + // because available mana calculation does not work correctly with Mana Flare we have to tap the land manually + activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add {B} to your mana pool"); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Nantuko Shade"); + + setStopAt(2, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerB, "Nantuko Shade", 1); + + } + + +} diff --git a/Mage/src/mage/abilities/effects/common/AddManaOfAnyTypeProducedEffect.java b/Mage/src/mage/abilities/effects/common/AddManaOfAnyTypeProducedEffect.java index d07dd9f6f95..c230ee146f5 100644 --- a/Mage/src/mage/abilities/effects/common/AddManaOfAnyTypeProducedEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddManaOfAnyTypeProducedEffect.java @@ -54,7 +54,7 @@ public class AddManaOfAnyTypeProducedEffect extends ManaEffect { @Override public boolean apply(Game game, Ability source) { - Permanent permanent = game.getPermanent(this.targetPointer.getFirst(game, source)); + Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source)); if (permanent != null) { Player targetController = game.getPlayer(permanent.getControllerId()); if (targetController == null) { From 1456e0172cc0c5886c18bcb6458988b0f8cf844a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 4 Jun 2015 18:25:16 +0200 Subject: [PATCH 057/102] * Ojutai Exemplars - Fixed wrong tooltip text. --- Mage.Sets/src/mage/sets/dragonsoftarkir/OjutaiExemplars.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/OjutaiExemplars.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/OjutaiExemplars.java index a6f534e7851..dbbc8692aa3 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/OjutaiExemplars.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/OjutaiExemplars.java @@ -58,7 +58,7 @@ import mage.target.common.TargetCreaturePermanent; */ public class OjutaiExemplars extends CardImpl { - private static final FilterSpell filter = new FilterSpell("creature spell"); + private static final FilterSpell filter = new FilterSpell("a noncreature spell"); static { filter.add(Predicates.not(new CardTypePredicate(CardType.CREATURE))); From 3b9cb1dc6e91b07770e1afde109f6b32cd49f45a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 4 Jun 2015 19:37:54 +0200 Subject: [PATCH 058/102] Add land dialog is now also shown during free deck building. Added a set selection drop down box to select the set the lands are used from. --- .../client/deckeditor/DeckEditorPanel.java | 3 +- .../mage/client/dialog/AddLandDialog.form | 101 ++++++++---- .../mage/client/dialog/AddLandDialog.java | 150 +++++++++++------- .../mage/client/util/gui/ColorsChooser.java | 2 +- 4 files changed, 161 insertions(+), 95 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index 492de4ca325..b90a94d858a 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -186,6 +186,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { break; case FREE_BUILDING: this.btnSubmit.setVisible(false); + this.btnAddLand.setVisible(true); this.cardSelector.loadCards(this.bigCard); //this.cardTableSelector.loadCards(this.bigCard); this.btnExit.setVisible(true); @@ -796,7 +797,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { private void btnAddLandActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAddLandActionPerformed AddLandDialog addLand = new AddLandDialog(); - addLand.showDialog(deck); + addLand.showDialog(deck, mode); refreshDeck(); }//GEN-LAST:event_btnAddLandActionPerformed diff --git a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form index fa5f815e756..c5cfd836728 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.form @@ -29,9 +29,9 @@ - + - + @@ -39,34 +39,43 @@ + - - - - + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + - + - + - + @@ -75,6 +84,11 @@ + + + + + @@ -100,7 +114,7 @@ - + @@ -112,6 +126,23 @@ + + + + + + + + + + + + + + + + + @@ -124,6 +155,11 @@ + + + + + @@ -131,9 +167,16 @@ - + - + + + + + + + + @@ -148,16 +191,9 @@ - + - - - - - - - - + @@ -167,11 +203,6 @@ - - - - - diff --git a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java index 1c4a9c26973..4210bf4ffb8 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/AddLandDialog.java @@ -31,6 +31,9 @@ import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import javax.swing.DefaultComboBoxModel; import javax.swing.JLayeredPane; import mage.Mana; import mage.cards.Card; @@ -41,6 +44,7 @@ import mage.cards.repository.CardRepository; import mage.cards.repository.ExpansionInfo; import mage.cards.repository.ExpansionRepository; import mage.client.MageFrame; +import mage.client.constants.Constants.DeckEditorMode; import mage.constants.Rarity; /** @@ -60,45 +64,49 @@ public class AddLandDialog extends MageDialog { this.setModal(true); } - public void showDialog(Deck deck) { + public void showDialog(Deck deck, DeckEditorMode mode) { this.deck = deck; - - // decide from which sets basic lands are taken from - for (String setCode :deck.getExpansionSetCodes()) { - ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByCode(setCode); - if (expansionInfo != null && expansionInfo.hasBasicLands()) { - this.setCodesland.add(expansionInfo.getCode()); - } - } - - // if sets have no basic land, take land from block - if (this.setCodesland.isEmpty()) { + SortedSet landSets = new TreeSet<>(); + if (!mode.equals(DeckEditorMode.FREE_BUILDING)) { + // decide from which sets basic lands are taken from for (String setCode :deck.getExpansionSetCodes()) { ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByCode(setCode); - if (expansionInfo != null) { - List blockSets = ExpansionRepository.instance.getSetsFromBlock(expansionInfo.getBlockName()); - for (ExpansionInfo blockSet: blockSets) { - if (blockSet.hasBasicLands()) { - this.setCodesland.add(blockSet.getCode()); + if (expansionInfo != null && expansionInfo.hasBasicLands()) { + this.setCodesland.add(expansionInfo.getCode()); + landSets.add(expansionInfo.getName()); + } + } + + // if sets have no basic land, take land from block + if (this.setCodesland.isEmpty()) { + for (String setCode :deck.getExpansionSetCodes()) { + ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByCode(setCode); + if (expansionInfo != null) { + List blockSets = ExpansionRepository.instance.getSetsFromBlock(expansionInfo.getBlockName()); + for (ExpansionInfo blockSet: blockSets) { + if (blockSet.hasBasicLands()) { + this.setCodesland.add(blockSet.getCode()); + landSets.add(blockSet.getName()); + } } } } } - } - // if still no set with lands found, take one by random + } + // if still no set with lands found, add list of all available if (this.setCodesland.isEmpty()) { - // if sets have no basic lands and also it has no parent or parent has no lands get last set with lands - // select a set with basic lands by random - Random generator = new Random(); List basicLandSets = ExpansionRepository.instance.getSetsWithBasicLandsByReleaseDate(); - if (basicLandSets.size() > 0) { - this.setCodesland.add(basicLandSets.get(generator.nextInt(basicLandSets.size())).getCode()); + for (ExpansionInfo expansionInfo: basicLandSets) { + landSets.add(expansionInfo.getName()); } - } - - if (this.setCodesland.isEmpty()) { + } + if (landSets.isEmpty()) { throw new IllegalArgumentException("No set with basic land was found"); } + if(landSets.size() > 1) { + landSets.add(""); + } + cbLandSet.setModel(new DefaultComboBoxModel(landSets.toArray())); MageFrame.getDesktop().add(this, JLayeredPane.PALETTE_LAYER); this.setVisible(true); @@ -106,14 +114,22 @@ public class AddLandDialog extends MageDialog { private void addLands(String landName, int number) { Random random = new Random(); + String landSetName = (String) cbLandSet.getSelectedItem(); + CardCriteria criteria = new CardCriteria(); - if (!setCodesland.isEmpty()) { + if (landSetName.equals("")) { criteria.setCodes(setCodesland.toArray(new String[setCodesland.size()])); - } + } else { + ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByName(landSetName); + if (expansionInfo == null) { + throw new IllegalArgumentException("Code of Set " + landSetName + " not found"); + } + criteria.setCodes(expansionInfo.getCode()); + } criteria.rarities(Rarity.LAND).name(landName); List cards = CardRepository.instance.findCards(criteria); if (cards.isEmpty()) { - return; + throw new IllegalArgumentException("No basic lands found in Set: " + landSetName); } for (int i = 0; i < number; i++) { @@ -132,16 +148,18 @@ public class AddLandDialog extends MageDialog { private void initComponents() { jButton2 = new javax.swing.JButton(); + lblLandSet = new javax.swing.JLabel(); + cbLandSet = new javax.swing.JComboBox(); lblForest = new javax.swing.JLabel(); spnForest = new javax.swing.JSpinner(); - spnIsland = new javax.swing.JSpinner(); lblIsland = new javax.swing.JLabel(); + spnIsland = new javax.swing.JSpinner(); + lblMountain = new javax.swing.JLabel(); + spnMountain = new javax.swing.JSpinner(); lblPains = new javax.swing.JLabel(); spnPlains = new javax.swing.JSpinner(); - spnMountain = new javax.swing.JSpinner(); - lblMountain = new javax.swing.JLabel(); - spnSwamp = new javax.swing.JSpinner(); lblSwamp = new javax.swing.JLabel(); + spnSwamp = new javax.swing.JSpinner(); btnAdd = new javax.swing.JButton(); btnCancel = new javax.swing.JButton(); btnAutoAdd = new javax.swing.JButton(); @@ -150,26 +168,30 @@ public class AddLandDialog extends MageDialog { setTitle("Add Land"); + lblLandSet.setText("Set"); + + cbLandSet.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + lblForest.setText("Forest"); spnForest.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); + lblIsland.setText("Island"); + spnIsland.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); - lblIsland.setText("Island"); + lblMountain.setText("Mountain"); + + spnMountain.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); lblPains.setText("Plains"); spnPlains.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); - spnMountain.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); - - lblMountain.setText("Mountain"); + lblSwamp.setText("Swamp"); spnSwamp.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(0), Integer.valueOf(0), null, Integer.valueOf(1))); - lblSwamp.setText("Swamp"); - btnAdd.setText("Add"); btnAdd.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -197,40 +219,50 @@ public class AddLandDialog extends MageDialog { layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblIsland) .addComponent(lblMountain) - .addComponent(lblForest)) + .addComponent(lblForest) + .addComponent(lblLandSet)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) - .addComponent(spnMountain, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) - .addComponent(spnIsland) - .addComponent(spnForest))) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(lblPains) - .addGap(21, 21, 21) - .addComponent(spnPlains)) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(lblSwamp) - .addGap(14, 14, 14) - .addComponent(spnSwamp))) - .addGap(114, 114, 114)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(spnMountain, javax.swing.GroupLayout.DEFAULT_SIZE, 85, Short.MAX_VALUE) + .addComponent(spnIsland) + .addComponent(spnForest)) + .addComponent(cbLandSet, javax.swing.GroupLayout.PREFERRED_SIZE, 207, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(lblSwamp) + .addGap(14, 14, 14) + .addComponent(spnSwamp)) + .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() + .addComponent(lblPains) + .addGap(21, 21, 21) + .addComponent(spnPlains))) + .addGap(122, 122, 122))) + .addContainerGap()) .addGroup(layout.createSequentialGroup() .addComponent(btnCancel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(btnAutoAdd) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(btnAdd) - .addGap(0, 40, Short.MAX_VALUE)))) + .addGap(0, 0, Short.MAX_VALUE)))) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(cbLandSet, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lblLandSet)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(lblForest) .addComponent(spnForest, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) @@ -250,7 +282,7 @@ public class AddLandDialog extends MageDialog { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(lblSwamp) .addComponent(spnSwamp, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 38, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(btnAdd) .addComponent(btnCancel) @@ -322,9 +354,11 @@ public class AddLandDialog extends MageDialog { private javax.swing.JButton btnAdd; private javax.swing.JButton btnAutoAdd; private javax.swing.JButton btnCancel; + private javax.swing.JComboBox cbLandSet; private javax.swing.JButton jButton2; private javax.swing.JLabel lblForest; private javax.swing.JLabel lblIsland; + private javax.swing.JLabel lblLandSet; private javax.swing.JLabel lblMountain; private javax.swing.JLabel lblPains; private javax.swing.JLabel lblSwamp; diff --git a/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java b/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java index bea63c29f89..a2eae35b36d 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/ColorsChooser.java @@ -78,7 +78,7 @@ public class ColorsChooser extends JComboBox implements ListCellRenderer { } private void drawOn(JPanel panel, String value) { - List images = new ArrayList(); + List images = new ArrayList<>(); value = value.toUpperCase(); for (int i = 0; i < value.length(); i++) { char symbol = value.charAt(i); From a50447c84dfab948a9b8f120cd4745f4ec5d9c7c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Jun 2015 00:05:40 +0200 Subject: [PATCH 059/102] * Setting of mana automatic payment option is saved and restored now so persistent between games (fixes #1002). --- .../main/java/mage/client/dialog/PreferencesDialog.java | 3 +++ Mage.Client/src/main/java/mage/client/game/GamePanel.java | 7 ++++++- .../src/main/java/mage/client/game/PlayAreaPanel.java | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 3d9b563e14b..18c0d7f78c6 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -132,6 +132,9 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_STOP_ALL_MAIN_PHASES = "stopOnAllMainPhases"; public static final String KEY_STOP_ALL_END_PHASES = "stopOnAllEndPhases"; + // mana auto payment + public static final String KEY_GAME_MANA_AUTOPAYMENT = "gameManaAutopayment"; + // Size of frame to check if divider locations should be used public static final String KEY_MAGE_PANEL_LAST_SIZE = "gamepanelLastSize"; diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 2e7483b9c16..6c153c8a625 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -90,6 +90,7 @@ import mage.client.dialog.PickChoiceDialog; import mage.client.dialog.PickNumberDialog; import mage.client.dialog.PickPileDialog; import mage.client.dialog.PreferencesDialog; +import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT; import mage.client.dialog.ShowCardsDialog; import mage.client.game.FeedbackPanel.FeedbackMode; import mage.client.plugins.adapters.MageActionCallback; @@ -382,7 +383,8 @@ public final class GamePanel extends javax.swing.JPanel { this.gameChatPanel.connect(session.getGameChatId(gameId)); if (!session.joinGame(gameId)) { removeGame(); - } else { + } else { + // play start sound AudioManager.playYourGameStarted(); } } @@ -456,6 +458,9 @@ public final class GamePanel extends javax.swing.JPanel { public synchronized void init(GameView game) { addPlayers(game); + // default menu states + setMenuStates(PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT, "true").equals("true")); + updateGame(game); } diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java index cc92819b86c..1fc7bb38b9a 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java @@ -53,6 +53,7 @@ import mage.client.MageFrame; import mage.client.cards.BigCard; import mage.client.dialog.PreferencesDialog; import static mage.client.dialog.PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS; +import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT; import mage.constants.PlayerAction; import mage.view.PlayerView; @@ -220,6 +221,7 @@ public class PlayAreaPanel extends javax.swing.JPanel { @Override public void actionPerformed(ActionEvent e) { boolean manaPoolAutomatic = ((JCheckBoxMenuItem)e.getSource()).getState(); + PreferencesDialog.saveValue(KEY_GAME_MANA_AUTOPAYMENT, manaPoolAutomatic ? "true": "false"); gamePanel.setMenuStates(manaPoolAutomatic); gamePanel.getSession().sendPlayerAction(manaPoolAutomatic ? PlayerAction.MANA_AUTO_PAYMENT_ON: PlayerAction.MANA_AUTO_PAYMENT_OFF, gameId, null); } From 36eebfa317ef0780b29782d7def81e25f2258869 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Jun 2015 00:50:31 +0200 Subject: [PATCH 060/102] * Fixed that player enchnatments were not correctly removed as they left the battlefield causing problems if they were cast again later in the game (fixes #1006). --- .../sets/innistrad/CurseOfDeathsHold.java | 4 +- .../cards/abilities/curses/CursesTest.java | 59 ++++++++++++++++++- Mage/src/mage/game/GameImpl.java | 5 ++ Mage/src/mage/players/PlayerImpl.java | 8 ++- 4 files changed, 71 insertions(+), 5 deletions(-) diff --git a/Mage.Sets/src/mage/sets/innistrad/CurseOfDeathsHold.java b/Mage.Sets/src/mage/sets/innistrad/CurseOfDeathsHold.java index 1de846c25ec..2e1decc2ca4 100644 --- a/Mage.Sets/src/mage/sets/innistrad/CurseOfDeathsHold.java +++ b/Mage.Sets/src/mage/sets/innistrad/CurseOfDeathsHold.java @@ -54,7 +54,6 @@ public class CurseOfDeathsHold extends CardImpl { this.subtype.add("Aura"); this.subtype.add("Curse"); - // Enchant player TargetPlayer auraTarget = new TargetPlayer(); this.getSpellAbility().addTarget(auraTarget); @@ -106,5 +105,4 @@ class CurseOfDeathsHoldEffect extends ContinuousEffectImpl { public CurseOfDeathsHoldEffect copy() { return new CurseOfDeathsHoldEffect(this); } - -} \ No newline at end of file +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java index bf2e2bfe77e..48e4bbd1d9b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/curses/CursesTest.java @@ -232,6 +232,63 @@ public class CursesTest extends CardTestPlayerBase { assertLife(playerA, 20); assertLife(playerB, 20); assertPermanentCount(playerA, "Curse of Misfortunes", 1); - assertPermanentCount(playerA, "Curse of Bloodletting", 1); } + assertPermanentCount(playerA, "Curse of Bloodletting", 1); + } + + + @Test + public void testCurseOfDeathsHold() { + // Creatures enchanted player controls get -1/-1. + addCard(Zone.HAND, playerA, "Curse of Death's Hold"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB); + + setStopAt(1, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertPermanentCount(playerA, "Curse of Death's Hold", 1); + + assertPowerToughness(playerB, "Silvercoat Lion", 1, 1); + } + + @Test + public void testCurseOfDeathsHold2() { + // Creatures enchanted player controls get -1/-1. + addCard(Zone.HAND, playerA, "Curse of Death's Hold"); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 7); + addCard(Zone.BATTLEFIELD, playerA, "Forest", 2); + addCard(Zone.BATTLEFIELD, playerA, "Tasigur, the Golden Fang", 1); + + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1); + addCard(Zone.BATTLEFIELD, playerB, "Forest", 3); + addCard(Zone.HAND, playerB, "Reclamation Sage"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Reclamation Sage"); + addTarget(playerB, "Curse of Death's Hold"); + + // {2}{G/U}{G/U}: Put the top two cards of your library into your graveyard, then return a nonland card of an opponent's choice from your graveyard to your hand. + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{G/U}{G/U}: Put the top two cards"); + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Curse of Death's Hold", playerB); + + setStopAt(3, PhaseStep.END_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerB, "Reclamation Sage", 1); + assertPermanentCount(playerA, "Curse of Death's Hold", 1); + assertGraveyardCount(playerA, 2); + + assertPowerToughness(playerB, "Silvercoat Lion", 1, 1); + } + } diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 0de2767ce8b..db403a4264f 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -2058,6 +2058,11 @@ public abstract class GameImpl implements Game, Serializable { Permanent attachedTo = getPermanent(perm.getAttachedTo()); if (attachedTo != null) { attachedTo.removeAttachment(perm.getId(), this); + } else { + Player attachedToPlayer = getPlayer(perm.getAttachedTo()); + if (attachedToPlayer != null) { + attachedToPlayer.removeAttachment(perm.getId(), this); + } } } // check if it's a creature and must be removed from combat diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index ccb4b5fa2a2..6c0e63e201e 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -789,12 +789,17 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean removeFromBattlefield(Permanent permanent, Game game) { permanent.removeFromCombat(game, false); - game.getBattlefield().removePermanent(permanent.getId()); if (permanent.getAttachedTo() != null) { Permanent attachedTo = game.getPermanent(permanent.getAttachedTo()); if (attachedTo != null) { attachedTo.removeAttachment(permanent.getId(), game); + } else { + Player attachedToPlayer = game.getPlayer(permanent.getAttachedTo()); + if (attachedToPlayer != null) { + attachedToPlayer.removeAttachment(permanent.getId(), game); + } } + } if (permanent.getPairedCard() != null) { Permanent pairedCard = game.getPermanent(permanent.getPairedCard()); @@ -802,6 +807,7 @@ public abstract class PlayerImpl implements Player, Serializable { pairedCard.clearPairedCard(); } } + game.getBattlefield().removePermanent(permanent.getId()); return true; } From 263107abcae879351106d93248806485f3aba983 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Jun 2015 08:10:34 +0200 Subject: [PATCH 061/102] * Bazaar Trader - Fixed that the control effect was applied after the target left and returned to the battlefield. --- .../src/mage/sets/worldwake/BazaarTrader.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/worldwake/BazaarTrader.java b/Mage.Sets/src/mage/sets/worldwake/BazaarTrader.java index 217f44fa9ad..5eee4cece5c 100644 --- a/Mage.Sets/src/mage/sets/worldwake/BazaarTrader.java +++ b/Mage.Sets/src/mage/sets/worldwake/BazaarTrader.java @@ -36,6 +36,7 @@ import mage.constants.Rarity; import mage.constants.SubLayer; import mage.constants.Zone; import mage.MageInt; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.TapSourceCost; @@ -92,6 +93,8 @@ public class BazaarTrader extends CardImpl { class BazaarTraderEffect extends ContinuousEffectImpl { + MageObjectReference targetPermanentReference; + public BazaarTraderEffect() { super(Duration.Custom, Layer.ControlChangingEffects_2, SubLayer.NA, Outcome.GainControl); this.staticText = "Target player gains control of target artifact, creature, or land you control"; @@ -99,6 +102,7 @@ class BazaarTraderEffect extends ContinuousEffectImpl { public BazaarTraderEffect(final BazaarTraderEffect effect) { super(effect); + this.targetPermanentReference = effect.targetPermanentReference; } @Override @@ -106,12 +110,20 @@ class BazaarTraderEffect extends ContinuousEffectImpl { return new BazaarTraderEffect(this); } + @Override + public void init(Ability source, Game game) { + super.init(source, game); + targetPermanentReference = new MageObjectReference(source.getTargets().get(1).getFirstTarget(), game); + } + @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getFirstTarget()); - Permanent permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + Permanent permanent = targetPermanentReference.getPermanent(game); if (player != null && permanent != null) { - return permanent.changeControllerId(player.getId(), game); + return permanent.changeControllerId(player.getId(), game); + } else { + discard(); } return false; } From de264d309c58d26e9838453d10a5516fe4d896d5 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Jun 2015 08:14:43 +0200 Subject: [PATCH 062/102] * Pit Trap - Fixed tooltip text. --- Mage.Sets/src/mage/sets/urzassaga/PitTrap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/urzassaga/PitTrap.java b/Mage.Sets/src/mage/sets/urzassaga/PitTrap.java index 74821b57853..36b47519922 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/PitTrap.java +++ b/Mage.Sets/src/mage/sets/urzassaga/PitTrap.java @@ -49,7 +49,7 @@ import mage.target.common.TargetAttackingCreature; */ public class PitTrap extends CardImpl { - private static final FilterAttackingCreature filter = new FilterAttackingCreature("creature without flying"); + private static final FilterAttackingCreature filter = new FilterAttackingCreature("attacking creature without flying"); static { filter.add(Predicates.not(new AbilityPredicate(FlyingAbility.class))); From fb1ba5f3c87ececbe61e04a8457441163c89d8d3 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Jun 2015 08:18:05 +0200 Subject: [PATCH 063/102] * Bloodshot Cyclops - Added missing tap source cost. --- Mage.Sets/src/mage/sets/urzasdestiny/BloodshotCyclops.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/BloodshotCyclops.java b/Mage.Sets/src/mage/sets/urzasdestiny/BloodshotCyclops.java index 3a9f747a28c..1fa8d58f38f 100644 --- a/Mage.Sets/src/mage/sets/urzasdestiny/BloodshotCyclops.java +++ b/Mage.Sets/src/mage/sets/urzasdestiny/BloodshotCyclops.java @@ -37,6 +37,7 @@ import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.Cost; import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.game.Game; @@ -62,7 +63,8 @@ public class BloodshotCyclops extends CardImpl { // {T}, Sacrifice a creature: Bloodshot Cyclops deals damage equal to the sacrificed // creature's power to target creature or player. - SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BloodshotCyclopsEffect(), new SacrificeTargetCost(new TargetControlledCreaturePermanent())); + SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BloodshotCyclopsEffect(), new TapSourceCost()); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent())); ability.addTarget(new TargetCreatureOrPlayer()); this.addAbility(ability); } From 01c81c1cb19f8a0a14770d15f2480d9fed55c4e9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Jun 2015 11:44:51 +0200 Subject: [PATCH 064/102] * Homing Sliver - Fixed the gain Slivercycling ability. --- .../mage/sets/futuresight/HomingSliver.java | 83 ++++--------------- .../{CycleTest.java => CyclingTest.java} | 35 ++++++-- .../abilities/keyword/CyclingAbility.java | 6 +- Mage/src/mage/players/PlayerImpl.java | 2 +- 4 files changed, 52 insertions(+), 74 deletions(-) rename Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/{CycleTest.java => CyclingTest.java} (82%) diff --git a/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java b/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java index 35ec2cc9dcf..31541ddd248 100644 --- a/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java +++ b/Mage.Sets/src/mage/sets/futuresight/HomingSliver.java @@ -29,10 +29,9 @@ package mage.sets.futuresight; import java.util.UUID; import mage.MageInt; -import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; -import mage.abilities.costs.common.DiscardSourceCost; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.ContinuousEffectImpl; import mage.abilities.keyword.CyclingAbility; @@ -52,8 +51,9 @@ import mage.players.Player; /** * - * @author anonymous + * @author Luna Skyrise */ + public class HomingSliver extends CardImpl { private static final FilterCard filter = new FilterCard("Sliver card"); @@ -70,31 +70,10 @@ public class HomingSliver extends CardImpl { this.toughness = new MageInt(2); // Each Sliver card in each player's hand has slivercycling {3}. - Ability ability = new CyclingAbility(new ManaCostsImpl("{3}"), filter, "Slivercycling"); - ability.addCost(new DiscardSourceCost()); - this.addAbility(new SimpleStaticAbility(Zone.HAND, new HomingSliverEffect())); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new HomingSliverEffect())); + // Slivercycling {3} - this.addAbility(ability); - /** - * 01/02/2009 Slivercycling is a form of cycling. Any ability that - * triggers on a card being cycled also triggers on Slivercycling this - * card. Any ability that stops a cycling ability from being activated - * also stops Plainscycling from being activated. - */ - - /** - * 01/02/2009 Slivercycling is an activated ability. Effects that - * interact with activated abilities (such as Stifle or Rings of - * Brighthearth) will interact with Slivercycling. Effects that interact - * with spells (such as Remove Soul or Faerie Tauntings) will not. - */ - /** - * 01/02/2009 You can choose to find any card with the Sliver creature - * type, even if it isn't a creature card. This includes, for example, - * Tribal cards with the Changeling ability. You can also choose not to - * find a card, even if there is a Sliver card in your graveyard. - * - */ + this.addAbility(new CyclingAbility(new ManaCostsImpl("{3}"), filter, "Slivercycling")); } public HomingSliver(final HomingSliver card) { @@ -109,15 +88,15 @@ public class HomingSliver extends CardImpl { class HomingSliverEffect extends ContinuousEffectImpl { - private static final FilterCard filter2 = new FilterCard("Sliver card"); + private static final FilterCard filter = new FilterCard("Sliver card"); static { - filter2.add(new SubtypePredicate("Sliver")); + filter.add(new SubtypePredicate("Sliver")); } public HomingSliverEffect() { super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); - this.staticText = "Each Sliver card in each player's hand has slivercycling {3}"; + this.staticText = "each Sliver card in each player's hand has slivercycling {3}"; } public HomingSliverEffect(final HomingSliverEffect effect) { @@ -129,45 +108,19 @@ class HomingSliverEffect extends ContinuousEffectImpl { return new HomingSliverEffect(this); } - @Override - public void init(Ability source, Game game) { - super.init(source, game); - if (this.affectedObjectsSet) { - for (UUID p : game.getPlayerList()) { - Player player = game.getPlayer(p); - if (player != null) { - for (UUID cardId : player.getHand()) { - Card card = game.getCard(cardId); - if (card.getSubtype().contains("Sliver")) { - affectedObjectList.add(new MageObjectReference(card, game)); - } - } - } - } - } - } - @Override public boolean apply(Game game, Ability source) { - for (UUID p : game.getPlayerList()) { - Player player = game.getPlayer(p); - if (player != null) { - for (UUID cardId : player.getHand()) { - if (affectedObjectList.contains(new MageObjectReference(cardId, game))) { - Card card = game.getCard(cardId); - CyclingAbility ability = null; - if (card.hasSubtype("Sliver")) { - ability = new CyclingAbility(new ManaCostsImpl("{3}"), filter2, "Slivercycling"); - } - if (ability != null) { - ability.setSourceId(cardId); - ability.setControllerId(card.getOwnerId()); - game.getState().addOtherAbility(card, ability); - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + for (UUID playerId: controller.getInRange()) { + Player player = game.getPlayer(playerId); + if (player != null) { + for (Card card : player.getHand().getCards(filter, game)) { + game.getState().addOtherAbility(card, new CyclingAbility(new GenericManaCost(3), filter, "Slivercycling")); } - } - return true; + } } + return true; } return false; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CycleTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CyclingTest.java similarity index 82% rename from Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CycleTest.java rename to Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CyclingTest.java index a0f1f8e8dd5..e50598c40b4 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CycleTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CyclingTest.java @@ -37,7 +37,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase; * * @author LevelX2 */ -public class CycleTest extends CardTestPlayerBase { +public class CyclingTest extends CardTestPlayerBase { /** * 702.28. Cycling @@ -60,7 +60,7 @@ public class CycleTest extends CardTestPlayerBase { */ @Test - public void CycleAndTriggerTest() { + public void cycleAndTriggerTest() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); // Destroy all creatures. They can't be regenerated. Draw a card for each creature destroyed this way. // Cycling {3}{B}{B} @@ -82,15 +82,13 @@ public class CycleTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Pillarfield Ox", 1); assertPowerToughness(playerB, "Pillarfield Ox", 0, 2); - - } /** * Cycle from graveyard or battlefield may not work */ @Test - public void CycleFromGraveyard() { + public void cycleFromGraveyard() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); // Destroy all creatures. They can't be regenerated. Draw a card for each creature destroyed this way. // Cycling {3}{B}{B} @@ -113,5 +111,32 @@ public class CycleTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Disciple Of Grace", 1); } + + /** + * Type cycling for sliver + */ + @Test + public void cycleFromHomingSliver() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3); + // Each Sliver card in each player's hand has slivercycling {3}. + addCard(Zone.BATTLEFIELD, playerA, "Homing Sliver"); + // All Sliver creatures have flying. + addCard(Zone.HAND, playerA, "Winged Sliver"); + + addCard(Zone.LIBRARY, playerA, "Horned Sliver"); + addCard(Zone.LIBRARY, playerA, "Silvercoat Lion", 10); + skipInitShuffling(); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Slivercycling {3}"); + addTarget(playerA, "Horned Sliver"); + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertHandCount(playerA, 1); + + assertGraveyardCount(playerA, "Winged Sliver", 1); + + assertHandCount(playerA, "Horned Sliver", 1); // searched by slivercyclibng + } } diff --git a/Mage/src/mage/abilities/keyword/CyclingAbility.java b/Mage/src/mage/abilities/keyword/CyclingAbility.java index 27dcf0418ab..e52a77724c8 100644 --- a/Mage/src/mage/abilities/keyword/CyclingAbility.java +++ b/Mage/src/mage/abilities/keyword/CyclingAbility.java @@ -32,7 +32,7 @@ import mage.constants.Zone; import mage.abilities.ActivatedAbilityImpl; import mage.abilities.costs.Cost; import mage.abilities.costs.common.DiscardSourceCost; -import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.costs.mana.ManaCost; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect; import mage.filter.FilterCard; @@ -76,11 +76,11 @@ public class CyclingAbility extends ActivatedAbilityImpl { @Override public String getRule() { StringBuilder rule = new StringBuilder(this.text); - if(cost instanceof ManaCosts){ + if(cost instanceof ManaCost){ rule.append(" "); } else{ - rule.append(" - "); + rule.append("—"); } rule.append(cost.getText()).append(" (").append(super.getRule(true)).append(")"); return rule.toString(); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 6c0e63e201e..47e1de7eb57 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -2379,7 +2379,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (hidden) { for (Card card : hand.getUniqueCards(game)) { - for (Ability ability : card.getAbilities()) { // gets this activated ability from hand? (Morph?) + for (Ability ability : card.getAbilities(game)) { // gets this activated ability from hand? (Morph?) if (ability.getZone().match(Zone.HAND)) { if (ability instanceof ActivatedAbility) { if (!(ability instanceof PlayLandAbility) From 81187ba1c7f7d4b4fd711e4292fe4ed740a5163a Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Jun 2015 13:35:33 +0200 Subject: [PATCH 065/102] Fixed a problem with removing player attachments. --- .../cards/abilities/other/NecromancyTest.java | 4 ++-- Mage/src/mage/game/GameImpl.java | 2 +- Mage/src/mage/players/Player.java | 2 +- Mage/src/mage/players/PlayerImpl.java | 19 ++++++++----------- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java index 54b959bb00b..5b2a9ae2f6c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/other/NecromancyTest.java @@ -112,8 +112,8 @@ public class NecromancyTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, "Disenchant"); addCard(Zone.GRAVEYARD, playerA, "Craw Wurm"); - castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy"); - castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Disenchant", "Necromancy"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Necromancy"); // enchanting the Craw Wurm + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Disenchant", "Necromancy"); // if Necromancy leaves, the enchanted creature has to leave too setStopAt(1, PhaseStep.END_TURN); execute(); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index db403a4264f..bdd68d29771 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -2061,7 +2061,7 @@ public abstract class GameImpl implements Game, Serializable { } else { Player attachedToPlayer = getPlayer(perm.getAttachedTo()); if (attachedToPlayer != null) { - attachedToPlayer.removeAttachment(perm.getId(), this); + attachedToPlayer.removeAttachment(perm, this); } } } diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index 0e9adf9bd0e..e6c4338ab32 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -385,7 +385,7 @@ public interface Player extends MageItem, Copyable { void addCounters(Counter counter, Game game); List getAttachments(); boolean addAttachment(UUID permanentId, Game game); - boolean removeAttachment(UUID permanentId, Game game); + boolean removeAttachment(Permanent permanent, Game game); /** * Signals that the player becomes active player in this turn. diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 47e1de7eb57..cc5236331e0 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -771,15 +771,12 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public boolean removeAttachment(UUID permanentId, Game game) { - if (this.attachments.contains(permanentId)) { - Permanent aura = game.getPermanent(permanentId); - if (aura != null) { - if (!game.replaceEvent(new GameEvent(GameEvent.EventType.UNATTACH, playerId, permanentId, aura.getControllerId()))) { - this.attachments.remove(permanentId); - aura.attachTo(null, game); - } - game.fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, playerId, permanentId, aura.getControllerId())); + public boolean removeAttachment(Permanent attachment, Game game) { + if (this.attachments.contains(attachment.getId())) { + if (!game.replaceEvent(new GameEvent(GameEvent.EventType.UNATTACH, playerId, attachment.getId(), attachment.getControllerId()))) { + this.attachments.remove(attachment.getId()); + attachment.attachTo(null, game); + game.fireEvent(new GameEvent(GameEvent.EventType.UNATTACHED, playerId, attachment.getId(), attachment.getControllerId())); return true; } } @@ -789,6 +786,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean removeFromBattlefield(Permanent permanent, Game game) { permanent.removeFromCombat(game, false); + game.getBattlefield().removePermanent(permanent.getId()); if (permanent.getAttachedTo() != null) { Permanent attachedTo = game.getPermanent(permanent.getAttachedTo()); if (attachedTo != null) { @@ -796,7 +794,7 @@ public abstract class PlayerImpl implements Player, Serializable { } else { Player attachedToPlayer = game.getPlayer(permanent.getAttachedTo()); if (attachedToPlayer != null) { - attachedToPlayer.removeAttachment(permanent.getId(), game); + attachedToPlayer.removeAttachment(permanent, game); } } @@ -807,7 +805,6 @@ public abstract class PlayerImpl implements Player, Serializable { pairedCard.clearPairedCard(); } } - game.getBattlefield().removePermanent(permanent.getId()); return true; } From 43f79908b83d67e9b134a69df0af1ce6189dc744 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Jun 2015 13:39:42 +0200 Subject: [PATCH 066/102] Fixed a problem with tooltip text generation. --- .../abilities/effects/common/PreventDamageBySourceEffect.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java b/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java index 9845cd5824e..6090fcc2371 100644 --- a/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/PreventDamageBySourceEffect.java @@ -58,7 +58,7 @@ public class PreventDamageBySourceEffect extends PreventionEffectImpl { filterObject.setMessage(filterObject.getMessage() + " source"); } this.target = new TargetSource(filterObject); - staticText = "Prevent all damage " + filterObject.getMessage() + " source of your choice would deal this turn"; + staticText = "Prevent all damage " + filterObject.getMessage() + " of your choice would deal this turn"; } public PreventDamageBySourceEffect(final PreventDamageBySourceEffect effect) { From 6faf248273f6e72b40cbf661438a487cc4086396 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Fri, 5 Jun 2015 08:53:28 -0400 Subject: [PATCH 067/102] Abstracted out FilterArtifactSpell, and changed implementations that depended on their own instance of it. Golem Foundry uses already implemented, generic Golem token - instead of its own duplicate one. --- .../mage/sets/antiquities/ArtifactBlast.java | 11 +---- .../mage/sets/mirrodin/VedalkenArchmage.java | 12 +---- .../mage/sets/mirrodinbesieged/MirranSpy.java | 7 ++- .../sets/mirrodinbesieged/SteelSabotage.java | 13 +---- .../mage/sets/scarsofmirrodin/Embersmith.java | 13 +---- .../sets/scarsofmirrodin/GolemFoundry.java | 28 ++--------- .../mage/sets/scarsofmirrodin/HaltOrder.java | 15 ++---- .../mage/sets/scarsofmirrodin/Lifesmith.java | 12 +---- .../mage/sets/scarsofmirrodin/Myrsmith.java | 13 +---- .../mage/sets/scarsofmirrodin/Painsmith.java | 10 +--- .../sets/scarsofmirrodin/PrecursorGolem.java | 2 +- .../sets/scarsofmirrodin/Riddlesmith.java | 13 ++--- .../filter/common/FilterArtifactSpell.java | 48 +++++++++++++++++++ 13 files changed, 77 insertions(+), 120 deletions(-) create mode 100644 Mage/src/mage/filter/common/FilterArtifactSpell.java diff --git a/Mage.Sets/src/mage/sets/antiquities/ArtifactBlast.java b/Mage.Sets/src/mage/sets/antiquities/ArtifactBlast.java index a8f0dfe5f13..56d1cc45fe1 100644 --- a/Mage.Sets/src/mage/sets/antiquities/ArtifactBlast.java +++ b/Mage.Sets/src/mage/sets/antiquities/ArtifactBlast.java @@ -32,27 +32,20 @@ import mage.abilities.effects.common.CounterTargetEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; import mage.target.TargetSpell; +import mage.filter.common.FilterArtifactSpell; /** * * @author Jgod */ public class ArtifactBlast extends CardImpl { - private static final FilterSpell filter = new FilterSpell("artifact spell"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public ArtifactBlast(UUID ownerId) { super(ownerId, 87, "Artifact Blast", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}"); this.expansionSetCode = "ATQ"; // Counter target artifact spell. - this.getSpellAbility().addTarget(new TargetSpell(filter)); + this.getSpellAbility().addTarget(new TargetSpell(new FilterArtifactSpell())); this.getSpellAbility().addEffect(new CounterTargetEffect()); } diff --git a/Mage.Sets/src/mage/sets/mirrodin/VedalkenArchmage.java b/Mage.Sets/src/mage/sets/mirrodin/VedalkenArchmage.java index d4eb18b145b..5589557fd24 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/VedalkenArchmage.java +++ b/Mage.Sets/src/mage/sets/mirrodin/VedalkenArchmage.java @@ -34,22 +34,13 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterSpell; -import mage.filter.common.FilterArtifactCard; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; /** * * @author LevelX2 */ public class VedalkenArchmage extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public VedalkenArchmage(UUID ownerId) { super(ownerId, 55, "Vedalken Archmage", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{U}{U}"); this.expansionSetCode = "MRD"; @@ -60,6 +51,7 @@ public class VedalkenArchmage extends CardImpl { this.toughness = new MageInt(2); // Whenever you cast an artifact spell, draw a card. + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); this.addAbility(new SpellCastControllerTriggeredAbility(new DrawCardSourceControllerEffect(1), filter, false)); } diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/MirranSpy.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/MirranSpy.java index 8fb52da3e43..e2c07fb8cd4 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/MirranSpy.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/MirranSpy.java @@ -35,7 +35,7 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterSpell; +import mage.filter.common.FilterArtifactSpell; import mage.target.common.TargetCreaturePermanent; /** @@ -43,9 +43,7 @@ import mage.target.common.TargetCreaturePermanent; * @author North */ public class MirranSpy extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - + public MirranSpy(UUID ownerId) { super(ownerId, 26, "Mirran Spy", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{U}"); this.expansionSetCode = "MBS"; @@ -58,6 +56,7 @@ public class MirranSpy extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // Whenever you cast an artifact spell, you may untap target creature. + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); SpellCastControllerTriggeredAbility ability = new SpellCastControllerTriggeredAbility(new UntapTargetEffect(), filter, true); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/SteelSabotage.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/SteelSabotage.java index 701966d1efa..f43bfe117fe 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/SteelSabotage.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/SteelSabotage.java @@ -34,31 +34,22 @@ import mage.abilities.Mode; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; import mage.target.TargetSpell; import mage.target.common.TargetArtifactPermanent; +import mage.filter.common.FilterArtifactSpell; /** * * @author North */ public class SteelSabotage extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("artifact spell"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public SteelSabotage(UUID ownerId) { super(ownerId, 33, "Steel Sabotage", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{U}"); this.expansionSetCode = "MBS"; - // Choose one - Counter target artifact spell; or return target artifact to its owner's hand. this.getSpellAbility().addEffect(new CounterTargetEffect()); - this.getSpellAbility().addTarget(new TargetSpell(filter)); + this.getSpellAbility().addTarget(new TargetSpell(new FilterArtifactSpell())); Mode mode = new Mode(); mode.getEffects().add(new ReturnToHandTargetEffect()); mode.getTargets().add(new TargetArtifactPermanent()); diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Embersmith.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Embersmith.java index 4982994c785..09d83f6f150 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Embersmith.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Embersmith.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; @@ -40,8 +39,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.constants.Outcome; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -51,12 +49,6 @@ import mage.target.common.TargetCreatureOrPlayer; * @author Loki, North */ public class Embersmith extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Embersmith(UUID ownerId) { super(ownerId, 87, "Embersmith", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{R}"); this.expansionSetCode = "SOM"; @@ -66,6 +58,7 @@ public class Embersmith extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); SpellCastControllerTriggeredAbility ability = new SpellCastControllerTriggeredAbility(new EmbersmithEffect(), filter, false); ability.addTarget(new TargetCreatureOrPlayer()); this.addAbility(ability); @@ -79,7 +72,6 @@ public class Embersmith extends CardImpl { public Embersmith copy() { return new Embersmith(this); } - } class EmbersmithEffect extends OneShotEffect { @@ -116,5 +108,4 @@ class EmbersmithEffect extends OneShotEffect { public EmbersmithEffect copy() { return new EmbersmithEffect(this); } - } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/GolemFoundry.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/GolemFoundry.java index 59bc3d0c7d2..13d866c247a 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/GolemFoundry.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/GolemFoundry.java @@ -25,14 +25,12 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.MageInt; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.costs.common.RemoveCountersSourceCost; @@ -40,26 +38,22 @@ import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.counters.CounterType; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.game.permanent.token.Token; +import mage.filter.common.FilterArtifactSpell; +import mage.game.permanent.token.GolemToken; /** * * @author Loki, North */ public class GolemFoundry extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public GolemFoundry (UUID ownerId) { super(ownerId, 160, "Golem Foundry", Rarity.COMMON, new CardType[]{CardType.ARTIFACT}, "{3}"); this.expansionSetCode = "SOM"; + // Whenever you cast an artifact spell, + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); this.addAbility(new SpellCastControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), filter, true)); + // you may put a charge counter on Golem Foundry. this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new GolemToken()), new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(3)))); } @@ -71,16 +65,4 @@ public class GolemFoundry extends CardImpl { public GolemFoundry copy() { return new GolemFoundry(this); } - -} - -class GolemToken extends Token { - public GolemToken() { - super("Golem", "a 3/3 colorless Golem artifact creature token"); - cardType.add(CardType.ARTIFACT); - cardType.add(CardType.CREATURE); - subtype.add("Golem"); - power = new MageInt(3); - toughness = new MageInt(3); - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/HaltOrder.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/HaltOrder.java index 43863e366bd..3054ed9722b 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/HaltOrder.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/HaltOrder.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; @@ -34,8 +33,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.CounterTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; import mage.target.TargetSpell; /** @@ -43,18 +41,12 @@ import mage.target.TargetSpell; * @author Loki */ public class HaltOrder extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("artifact spell"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public HaltOrder (UUID ownerId) { super(ownerId, 34, "Halt Order", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{U}"); this.expansionSetCode = "SOM"; - this.getSpellAbility().addTarget(new TargetSpell(filter)); + // Counter target artifact spell. Draw a card. + this.getSpellAbility().addTarget(new TargetSpell(new FilterArtifactSpell())); this.getSpellAbility().addEffect(new CounterTargetEffect()); this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } @@ -67,5 +59,4 @@ public class HaltOrder extends CardImpl { public HaltOrder copy() { return new HaltOrder(this); } - } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Lifesmith.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Lifesmith.java index 778fd03b286..ba2a2ca72e3 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Lifesmith.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Lifesmith.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; @@ -40,8 +39,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.constants.Outcome; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; import mage.game.Game; import mage.players.Player; @@ -50,12 +48,6 @@ import mage.players.Player; * @author Loki */ public class Lifesmith extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Lifesmith (UUID ownerId) { super(ownerId, 124, "Lifesmith", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{G}"); this.expansionSetCode = "SOM"; @@ -65,6 +57,7 @@ public class Lifesmith extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); this.addAbility(new SpellCastControllerTriggeredAbility(new LifesmithEffect(), filter, false)); } @@ -106,5 +99,4 @@ class LifesmithEffect extends OneShotEffect { public LifesmithEffect copy() { return new LifesmithEffect(this); } - } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Myrsmith.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Myrsmith.java index 29371f63ce1..7e1ebce026c 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Myrsmith.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Myrsmith.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; @@ -40,8 +39,7 @@ import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.constants.Outcome; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; import mage.game.Game; import mage.game.permanent.token.MyrToken; @@ -50,12 +48,6 @@ import mage.game.permanent.token.MyrToken; * @author Loki, North */ public class Myrsmith extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Myrsmith (UUID ownerId) { super(ownerId, 16, "Myrsmith", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.expansionSetCode = "SOM"; @@ -65,6 +57,7 @@ public class Myrsmith extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); this.addAbility(new SpellCastControllerTriggeredAbility(new MyrsmithEffect(), filter, false)); } @@ -76,7 +69,6 @@ public class Myrsmith extends CardImpl { public Myrsmith copy() { return new Myrsmith(this); } - } class MyrsmithEffect extends OneShotEffect { @@ -103,5 +95,4 @@ class MyrsmithEffect extends OneShotEffect { public MyrsmithEffect copy() { return new MyrsmithEffect(this); } - } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Painsmith.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Painsmith.java index 1ef30490727..e8e24290811 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Painsmith.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Painsmith.java @@ -38,8 +38,7 @@ import mage.abilities.effects.common.continuous.BoostTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.cards.CardImpl; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; import mage.target.common.TargetCreaturePermanent; /** @@ -47,12 +46,6 @@ import mage.target.common.TargetCreaturePermanent; * @author Loki, North */ public class Painsmith extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Painsmith (UUID ownerId) { super(ownerId, 74, "Painsmith", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{B}"); this.expansionSetCode = "SOM"; @@ -62,6 +55,7 @@ public class Painsmith extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + FilterArtifactSpell filter = new FilterArtifactSpell("an artifact spell"); SpellCastControllerTriggeredAbility ability = new SpellCastControllerTriggeredAbility(new BoostTargetEffect(2, 0, Duration.EndOfTurn), filter, true); ability.addEffect(new GainAbilityTargetEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn)); ability.addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java index 8c00484bf91..0332d1c5eea 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/PrecursorGolem.java @@ -46,6 +46,7 @@ import java.util.UUID; import mage.abilities.effects.common.CopySpellForEachItCouldTargetEffect; import mage.filter.FilterInPlay; import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.token.GolemToken; import mage.util.TargetAddress; /** @@ -140,7 +141,6 @@ class PrecursorGolemCopyTriggeredAbility extends TriggeredAbilityImpl { } } - class PrecursorGolemCopySpellEffect extends CopySpellForEachItCouldTargetEffect { public PrecursorGolemCopySpellEffect() { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Riddlesmith.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Riddlesmith.java index 11588e86349..344653885b5 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Riddlesmith.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Riddlesmith.java @@ -35,20 +35,13 @@ import mage.MageInt; import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.DrawDiscardControllerEffect; import mage.cards.CardImpl; -import mage.filter.FilterSpell; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactSpell; /** * * @author Loki, North */ public class Riddlesmith extends CardImpl { - - private static final FilterSpell filter = new FilterSpell("an artifact spell"); - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Riddlesmith (UUID ownerId) { super(ownerId, 40, "Riddlesmith", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.expansionSetCode = "SOM"; @@ -58,7 +51,8 @@ public class Riddlesmith extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); - this.addAbility(new SpellCastControllerTriggeredAbility(new DrawDiscardControllerEffect(), filter, true)); + // Whenever you cast an artifact spell, you may draw a card. If you do, discard a card. + this.addAbility(new SpellCastControllerTriggeredAbility(new DrawDiscardControllerEffect(), new FilterArtifactSpell("an artifact spell"), true)); } public Riddlesmith (final Riddlesmith card) { @@ -69,5 +63,4 @@ public class Riddlesmith extends CardImpl { public Riddlesmith copy() { return new Riddlesmith(this); } - } diff --git a/Mage/src/mage/filter/common/FilterArtifactSpell.java b/Mage/src/mage/filter/common/FilterArtifactSpell.java new file mode 100644 index 00000000000..530c21d6a69 --- /dev/null +++ b/Mage/src/mage/filter/common/FilterArtifactSpell.java @@ -0,0 +1,48 @@ +/* + * 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 mage.filter.common; + +import mage.constants.CardType; +import mage.filter.FilterSpell; +import mage.filter.predicate.mageobject.CardTypePredicate; + +/** + * + * @author Jgod + */ +public class FilterArtifactSpell extends FilterSpell { + + public FilterArtifactSpell() { + this("artifact spell"); + } + + public FilterArtifactSpell(String name) { + super(name); + this.add(new CardTypePredicate(CardType.ARTIFACT)); + } +} From 986995d154d22bb999751e4ad771e0d43f430198 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 5 Jun 2015 17:48:36 +0200 Subject: [PATCH 068/102] Some golem token image handling. --- Mage.Sets/src/mage/sets/fifthdawn/GuardianIdol.java | 6 +++--- Mage.Sets/src/mage/sets/mirrodinbesieged/TitanForge.java | 6 +++--- Mage.Sets/src/mage/sets/newphyrexia/BladeSplicer.java | 6 +++++- Mage.Sets/src/mage/sets/newphyrexia/ConversionChamber.java | 2 +- Mage.Sets/src/mage/sets/newphyrexia/MasterSplicer.java | 2 +- Mage.Sets/src/mage/sets/newphyrexia/MaulSplicer.java | 5 ++++- Mage.Sets/src/mage/sets/newphyrexia/SensorSplicer.java | 5 ++++- Mage.Sets/src/mage/sets/newphyrexia/VitalSplicer.java | 2 +- Mage.Sets/src/mage/sets/newphyrexia/WingSplicer.java | 5 ++++- Mage.Sets/src/mage/sets/theros/HammerOfPurphoros.java | 4 ++-- Mage/src/mage/game/permanent/token/GolemToken.java | 5 +++++ 11 files changed, 33 insertions(+), 15 deletions(-) diff --git a/Mage.Sets/src/mage/sets/fifthdawn/GuardianIdol.java b/Mage.Sets/src/mage/sets/fifthdawn/GuardianIdol.java index e8c9eec046f..2202874d7f2 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/GuardianIdol.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/GuardianIdol.java @@ -56,7 +56,7 @@ public class GuardianIdol extends CardImpl { // {tap}: Add {1} to your mana pool. this.addAbility(new ColorlessManaAbility()); // {2}: Guardian Idol becomes a 2/2 Golem artifact creature until end of turn. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new GolemToken(), "", Duration.EndOfTurn), new ManaCostsImpl("{2}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesCreatureSourceEffect(new GuardianIdolGolemToken(), "", Duration.EndOfTurn), new ManaCostsImpl("{2}"))); } public GuardianIdol(final GuardianIdol card) { @@ -69,9 +69,9 @@ public class GuardianIdol extends CardImpl { } } -class GolemToken extends Token { +class GuardianIdolGolemToken extends Token { - public GolemToken() { + public GuardianIdolGolemToken() { super("Golem", "a 2/2 Golem artifact creature token"); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/TitanForge.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/TitanForge.java index 93fba1c2065..d748d3754ee 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/TitanForge.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/TitanForge.java @@ -58,7 +58,7 @@ public class TitanForge extends CardImpl { ability.addCost(new TapSourceCost()); this.addAbility(ability); - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new GolemToken()), new TapSourceCost()); + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new TitanForgeGolemToken()), new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance(3))); this.addAbility(ability); @@ -75,8 +75,8 @@ public class TitanForge extends CardImpl { } -class GolemToken extends Token { - GolemToken() { +class TitanForgeGolemToken extends Token { + TitanForgeGolemToken() { super("Golem", "a 9/9 colorless Golem artifact creature token"); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); diff --git a/Mage.Sets/src/mage/sets/newphyrexia/BladeSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/BladeSplicer.java index d1ab3a10b54..ffa7e29184d 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/BladeSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/BladeSplicer.java @@ -66,7 +66,11 @@ public class BladeSplicer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken()))); + + // When Blade Splicer enters the battlefield, put a 3/3 colorless Golem artifact creature token onto the battlefield. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode)))); + + // Golem creatures you control have first strike. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter))); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/ConversionChamber.java b/Mage.Sets/src/mage/sets/newphyrexia/ConversionChamber.java index 78981ee0fc6..bbe38bfc816 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/ConversionChamber.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/ConversionChamber.java @@ -62,7 +62,7 @@ public class ConversionChamber extends CardImpl { ability.addCost(new TapSourceCost()); this.addAbility(ability); // {2}, {T}, Remove a charge counter from Conversion Chamber: Put a 3/3 colorless Golem artifact creature token onto the battlefield. - ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new GolemToken()), new GenericManaCost(2)); + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new GolemToken(expansionSetCode)), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.createInstance())); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/newphyrexia/MasterSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/MasterSplicer.java index 25d074d14d6..d59171cda89 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/MasterSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/MasterSplicer.java @@ -63,7 +63,7 @@ public class MasterSplicer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken()))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode)))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(1, 1, Duration.WhileOnBattlefield, filter, false))); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/MaulSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/MaulSplicer.java index 032799a9258..e928b5e74f9 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/MaulSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/MaulSplicer.java @@ -66,7 +66,10 @@ public class MaulSplicer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(), 2))); + // When Maul Splicer enters the battlefield, put two 3/3 colorless Golem artifact creature tokens onto the battlefield. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode), 2))); + + // Golem creatures you control have trample. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter))); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/SensorSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/SensorSplicer.java index e0bdbc916ca..6a360c51e5b 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/SensorSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/SensorSplicer.java @@ -65,7 +65,10 @@ public class SensorSplicer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken()))); + // When Sensor Splicer enters the battlefield, put a 3/3 colorless Golem artifact creature token onto the battlefield. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode)))); + + // Golem creatures you control have vigilance. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(VigilanceAbility.getInstance(), Duration.WhileOnBattlefield, filter))); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/VitalSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/VitalSplicer.java index 251e9e2f4a2..48400adbe7e 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/VitalSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/VitalSplicer.java @@ -71,7 +71,7 @@ public class VitalSplicer extends CardImpl { this.toughness = new MageInt(1); // When Vital Splicer enters the battlefield, put a 3/3 colorless Golem artifact creature token onto the battlefield. - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken()))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode)))); // {1}: Regenerate target Golem you control. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateTargetEffect(), new ManaCostsImpl("{1}")); diff --git a/Mage.Sets/src/mage/sets/newphyrexia/WingSplicer.java b/Mage.Sets/src/mage/sets/newphyrexia/WingSplicer.java index 417ad19da78..1716220359a 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/WingSplicer.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/WingSplicer.java @@ -66,7 +66,10 @@ public class WingSplicer extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken()))); + // When Wing Splicer enters the battlefield, put a 3/3 colorless Golem artifact creature token onto the battlefield. + this.addAbility(new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new GolemToken(expansionSetCode)))); + + // Golem creatures you control have flying. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(FlyingAbility.getInstance(), Duration.WhileOnBattlefield, filter))); } diff --git a/Mage.Sets/src/mage/sets/theros/HammerOfPurphoros.java b/Mage.Sets/src/mage/sets/theros/HammerOfPurphoros.java index 640ee18e60b..15d29e26ed8 100644 --- a/Mage.Sets/src/mage/sets/theros/HammerOfPurphoros.java +++ b/Mage.Sets/src/mage/sets/theros/HammerOfPurphoros.java @@ -45,7 +45,6 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.filter.common.FilterControlledLandPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.game.permanent.token.SaprolingToken; import mage.game.permanent.token.Token; import mage.target.common.TargetControlledPermanent; @@ -60,10 +59,10 @@ public class HammerOfPurphoros extends CardImpl { this.expansionSetCode = "THS"; this.supertype.add("Legendary"); - // Creatures you control have haste. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, new FilterCreaturePermanent("Creatures")))); + // {2}{R}, {tap}, Sacrifice a land: Put a 3/3 colorless Golem enchantment artifact creature token onto the battlefield. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new HammerOfPurphorosGolemToken()), new ManaCostsImpl("{2}{R}")); ability.addCost(new TapSourceCost()); @@ -84,6 +83,7 @@ class HammerOfPurphorosGolemToken extends Token { public HammerOfPurphorosGolemToken() { super("Golem", "3/3 colorless Golem enchantment artifact creature token"); + setOriginalExpansionSetCode("THS"); cardType.add(CardType.ENCHANTMENT); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); diff --git a/Mage/src/mage/game/permanent/token/GolemToken.java b/Mage/src/mage/game/permanent/token/GolemToken.java index 0c967c6dd0d..273943d7fa1 100644 --- a/Mage/src/mage/game/permanent/token/GolemToken.java +++ b/Mage/src/mage/game/permanent/token/GolemToken.java @@ -37,7 +37,12 @@ import mage.MageInt; public class GolemToken extends Token { public GolemToken() { + this("SOM"); + } + + public GolemToken(String setCode) { super("Golem", "a 3/3 colorless Golem artifact creature token"); + setOriginalExpansionSetCode(setCode); cardType.add(CardType.ARTIFACT); cardType.add(CardType.CREATURE); subtype.add("Golem"); From bea1cee395a9b0765fdbee1eaaed5c2e71ec723d Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Fri, 5 Jun 2015 12:05:51 -0400 Subject: [PATCH 069/102] Refactored a lot of the artifact/enchantment destruction. Using common filter types to reduce code duplication. --- .gitignore | 3 +- .../sets/alarareborn/QasaliPridemage.java | 17 ++--------- .../sets/alarareborn/VithianRenegades.java | 15 +++------- .../mage/sets/apocalypse/IllusionReality.java | 2 -- .../mage/sets/apocalypse/OrimsThunder.java | 17 ++--------- Mage.Sets/src/mage/sets/apocalypse/Smash.java | 4 --- .../mage/sets/avacynrestored/NaturalEnd.java | 15 ++-------- .../betrayersofkamigawa/TerashisGrasp.java | 16 ++-------- .../sets/championsofkamigawa/WearAway.java | 12 ++------ .../src/mage/sets/commander/AuraShards.java | 19 +++--------- .../commander2014/FreyaliseLlanowarsFury.java | 13 ++------ .../mage/sets/conflux/FiligreeFracture.java | 15 ++-------- .../mage/sets/darkascension/TorchFiend.java | 1 - .../sets/darkascension/WerewolfRansacker.java | 15 +++------- .../src/mage/sets/darksteel/Oxidize.java | 14 ++------- .../mage/sets/darksteel/ViridianZealot.java | 19 +++--------- .../sets/dissension/IndrikStomphowler.java | 15 ++-------- .../mage/sets/fifthdawn/TelJiladJustice.java | 1 - .../mage/sets/guildpact/ShatteringSpree.java | 1 - .../mage/sets/innistrad/AncientGrudge.java | 13 ++------ .../mage/sets/invasion/DismantlingBlow.java | 15 ++-------- .../mage/sets/khansoftarkir/SultaiCharm.java | 2 -- .../mage/sets/magic2010/SolemnOffering.java | 16 ++-------- .../src/mage/sets/magic2011/ManicVandal.java | 13 ++------ Mage.Sets/src/mage/sets/magic2013/Smelt.java | 1 - Mage.Sets/src/mage/sets/mirrodin/Shatter.java | 13 ++------ .../mirrodinbesieged/ViridianCorrupter.java | 14 ++------- .../mage/sets/nemesis/SealOfCleansing.java | 16 ++-------- .../mage/sets/odyssey/RayOfDistortion.java | 13 ++------ .../sets/planarchaos/SealOfPrimordium.java | 16 ++-------- .../sets/planechase2012/NullmageAdvocate.java | 12 ++------ .../mage/sets/ravnica/NullmageShepherd.java | 11 ++----- .../src/mage/sets/ravnica/SunderingVitae.java | 15 ++-------- .../sets/returntoravnica/RakdosCharm.java | 3 -- .../sets/saviorsofkamigawa/RendingVines.java | 1 - .../mage/sets/saviorsofkamigawa/YukiOnna.java | 15 ++-------- .../sets/scarsofmirrodin/SliceinTwain.java | 14 ++------- .../sets/scarsofmirrodin/SylvokReplica.java | 17 +++-------- .../mage/sets/shadowmoor/GleefulSabotage.java | 16 ++-------- .../sets/shadowmoor/SmashToSmithereens.java | 1 - .../sets/shardsofalara/DispellersCapsule.java | 17 ++--------- .../src/mage/sets/tempest/Disenchant.java | 15 ++-------- .../src/mage/sets/tempest/Verdigris.java | 11 ++----- .../mage/sets/tenthedition/Naturalize.java | 16 ++-------- .../src/mage/sets/theros/ArtisansSorrow.java | 13 ++------ .../mage/sets/theros/DestructiveRevelry.java | 12 ++------ .../mage/sets/timespiral/HarmonicSliver.java | 11 ++----- .../src/mage/sets/timespiral/KrosanGrip.java | 15 ++-------- .../sets/urzaslegacy/ViashinoHeretic.java | 1 - Mage.Sets/src/mage/sets/urzassaga/Scrap.java | 12 ++------ .../sets/vintagemasters/DevoutWitness.java | 14 ++------- .../mage/sets/weatherlight/AuraOfSilence.java | 15 ++-------- .../src/mage/sets/worldwake/NaturesClaim.java | 17 ++--------- .../mage/sets/zendikar/KorSanctifiers.java | 16 ++-------- .../src/mage/sets/zendikar/RelicCrush.java | 14 ++------- .../FilterArtifactOrEnchantmentPermanent.java | 30 +++++++++++++++---- 56 files changed, 130 insertions(+), 550 deletions(-) diff --git a/.gitignore b/.gitignore index 2d4ddf9ef24..812f56fc36d 100644 --- a/.gitignore +++ b/.gitignore @@ -50,7 +50,8 @@ Mage.Updater/target mage.updater.client/target releases -Utils/author.txt +Utils/author.txt +.DS_Store .metadata .project .settings diff --git a/Mage.Sets/src/mage/sets/alarareborn/QasaliPridemage.java b/Mage.Sets/src/mage/sets/alarareborn/QasaliPridemage.java index bccf5694cd9..392f4efd5cb 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/QasaliPridemage.java +++ b/Mage.Sets/src/mage/sets/alarareborn/QasaliPridemage.java @@ -39,9 +39,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.ExaltedAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.Target; import mage.target.TargetPermanent; @@ -51,22 +49,11 @@ import mage.target.TargetPermanent; */ public class QasaliPridemage extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public QasaliPridemage(UUID ownerId) { super(ownerId, 75, "Qasali Pridemage", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{G}{W}"); this.expansionSetCode = "ARB"; this.subtype.add("Cat"); this.subtype.add("Wizard"); - - - this.power = new MageInt(2); this.toughness = new MageInt(2); @@ -76,7 +63,7 @@ public class QasaliPridemage extends CardImpl { // {1}, Sacrifice Qasali Pridemage: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{1}")); ability.addCost(new SacrificeSourceCost()); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactOrEnchantmentPermanent()); ability.addTarget(target); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/alarareborn/VithianRenegades.java b/Mage.Sets/src/mage/sets/alarareborn/VithianRenegades.java index 292651c14d8..3f693142a46 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/VithianRenegades.java +++ b/Mage.Sets/src/mage/sets/alarareborn/VithianRenegades.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.alarareborn; import java.util.UUID; @@ -36,8 +35,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -45,11 +43,6 @@ import mage.target.TargetPermanent; * @author Loki */ public class VithianRenegades extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } public VithianRenegades (UUID ownerId) { super(ownerId, 64, "Vithian Renegades", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{R}{G}"); @@ -57,11 +50,12 @@ public class VithianRenegades extends CardImpl { this.subtype.add("Human"); this.subtype.add("Shaman"); - this.power = new MageInt(3); this.toughness = new MageInt(2); + + // When Vithian Renegades enters the battlefield, destroy target artifact. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactPermanent())); this.addAbility(ability); } @@ -73,5 +67,4 @@ public class VithianRenegades extends CardImpl { public VithianRenegades copy() { return new VithianRenegades(this); } - } diff --git a/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java b/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java index dd3af7ac036..6a875b15cb1 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java +++ b/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.apocalypse; import java.util.UUID; @@ -65,7 +64,6 @@ public class IllusionReality extends SplitCard { getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addTarget(new TargetArtifactPermanent()); getRightHalfCard().getSpellAbility().addEffect(new DestroyTargetEffect()); - } public IllusionReality(final IllusionReality card) { diff --git a/Mage.Sets/src/mage/sets/apocalypse/OrimsThunder.java b/Mage.Sets/src/mage/sets/apocalypse/OrimsThunder.java index 764f0b9a0a0..9300329a73b 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/OrimsThunder.java +++ b/Mage.Sets/src/mage/sets/apocalypse/OrimsThunder.java @@ -28,7 +28,6 @@ package mage.sets.apocalypse; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; @@ -41,9 +40,7 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -55,30 +52,20 @@ import mage.target.common.TargetCreaturePermanent; */ public class OrimsThunder extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public OrimsThunder(UUID ownerId) { super(ownerId, 15, "Orim's Thunder", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{W}"); this.expansionSetCode = "APC"; - // Kicker {R} this.addAbility(new KickerAbility("{R}")); // Destroy target artifact or enchantment. If Orim's Thunder was kicked, it deals damage equal to that permanent's converted mana cost to target creature. this.getSpellAbility().addEffect(new OrimsThunderEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new OrimsThunderEffect2(), KickedCondition.getInstance(), "If Orim's Thunder was kicked, it deals damage equal to that permanent's converted mana cost to target creature")); - } @Override diff --git a/Mage.Sets/src/mage/sets/apocalypse/Smash.java b/Mage.Sets/src/mage/sets/apocalypse/Smash.java index 255670b260a..d744e0f308a 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/Smash.java +++ b/Mage.Sets/src/mage/sets/apocalypse/Smash.java @@ -28,15 +28,12 @@ package mage.sets.apocalypse; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; import mage.filter.common.FilterArtifactPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; import mage.target.TargetPermanent; /** @@ -48,7 +45,6 @@ public class Smash extends CardImpl { super(ownerId, 69, "Smash", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{R}"); this.expansionSetCode = "APC"; - // Destroy target artifact. this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); diff --git a/Mage.Sets/src/mage/sets/avacynrestored/NaturalEnd.java b/Mage.Sets/src/mage/sets/avacynrestored/NaturalEnd.java index 2075204aa25..2f177aa6106 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/NaturalEnd.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/NaturalEnd.java @@ -33,9 +33,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -44,23 +42,14 @@ import mage.target.TargetPermanent; */ public class NaturalEnd extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public NaturalEnd(UUID ownerId) { super(ownerId, 185, "Natural End", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{G}"); this.expansionSetCode = "AVR"; - // Destroy target artifact or enchantment. You gain 3 life. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new GainLifeEffect(3)); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); } public NaturalEnd(final NaturalEnd card) { diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/TerashisGrasp.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/TerashisGrasp.java index 355f5ce0c63..92340a737db 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/TerashisGrasp.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/TerashisGrasp.java @@ -28,7 +28,6 @@ package mage.sets.betrayersofkamigawa; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.Ability; @@ -36,9 +35,7 @@ import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Outcome; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -50,22 +47,13 @@ import mage.target.TargetPermanent; */ public class TerashisGrasp extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public TerashisGrasp(UUID ownerId) { super(ownerId, 26, "Terashi's Grasp", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{2}{W}"); this.expansionSetCode = "BOK"; this.subtype.add("Arcane"); - // Destroy target artifact or enchantment. - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); // You gain life equal to its converted mana cost. this.getSpellAbility().addEffect(new TerashisGraspEffect()); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/WearAway.java b/Mage.Sets/src/mage/sets/championsofkamigawa/WearAway.java index 0c7c0cf220a..b831a1fa4dd 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/WearAway.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/WearAway.java @@ -33,9 +33,7 @@ import mage.abilities.keyword.SpliceOntoArcaneAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.Target; import mage.target.TargetPermanent; @@ -45,20 +43,14 @@ import mage.target.TargetPermanent; */ public class WearAway extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); - } - public WearAway(UUID ownerId) { super(ownerId, 250, "Wear Away", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G}{G}"); this.expansionSetCode = "CHK"; this.subtype.add("Arcane"); - // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactOrEnchantmentPermanent()); this.getSpellAbility().addTarget(target); // Splice onto Arcane {3}{G} this.addAbility(new SpliceOntoArcaneAbility("{3}{G}")); diff --git a/Mage.Sets/src/mage/sets/commander/AuraShards.java b/Mage.Sets/src/mage/sets/commander/AuraShards.java index c61c4ec75f8..3827c941bb4 100644 --- a/Mage.Sets/src/mage/sets/commander/AuraShards.java +++ b/Mage.Sets/src/mage/sets/commander/AuraShards.java @@ -31,37 +31,26 @@ import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.target.TargetPermanent; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; -import mage.target.TargetPermanent; /** * * @author emerald000 */ public class AuraShards extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public AuraShards(UUID ownerId) { super(ownerId, 182, "Aura Shards", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}{W}"); this.expansionSetCode = "CMD"; - // Whenever a creature enters the battlefield under your control, you may destroy target artifact or enchantment. Ability ability = new EntersBattlefieldControlledTriggeredAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new FilterCreaturePermanent(), true, "Whenever a creature enters the battlefield under your control, you may destroy target artifact or enchantment"); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/commander2014/FreyaliseLlanowarsFury.java b/Mage.Sets/src/mage/sets/commander2014/FreyaliseLlanowarsFury.java index 9a85986a5f4..adf5daa6ca5 100644 --- a/Mage.Sets/src/mage/sets/commander2014/FreyaliseLlanowarsFury.java +++ b/Mage.Sets/src/mage/sets/commander2014/FreyaliseLlanowarsFury.java @@ -43,10 +43,8 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.counters.CounterType; -import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.permanent.token.Token; import mage.target.TargetPermanent; @@ -57,13 +55,8 @@ import mage.target.TargetPermanent; */ public class FreyaliseLlanowarsFury extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); private static final FilterControlledCreaturePermanent filterGreen = new FilterControlledCreaturePermanent("green creature you control"); - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); filterGreen.add(new ColorPredicate(ObjectColor.GREEN)); } @@ -72,21 +65,19 @@ public class FreyaliseLlanowarsFury extends CardImpl { this.expansionSetCode = "C14"; this.subtype.add("Freyalise"); - this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(3)), false)); // +2: Put a 1/1 green Elf Druid creature token onto the battlefield with "{T}: Add {G} to your mana pool." this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new FreyaliseLlanowarsFuryToken()), 2)); // -2: Destroy target artifact or enchantment. LoyaltyAbility loyaltyAbility = new LoyaltyAbility(new DestroyTargetEffect(), -2); - loyaltyAbility.addTarget(new TargetPermanent(filter)); + loyaltyAbility.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(loyaltyAbility); // -6: Draw a card for each green creature you control. this.addAbility(new LoyaltyAbility(new DrawCardSourceControllerEffect(new PermanentsOnBattlefieldCount(filterGreen)), -6)); // Freyalise, Llanowar's Fury can be your commander. this.addAbility(CanBeYourCommanderAbility.getInstance()); - } public FreyaliseLlanowarsFury(final FreyaliseLlanowarsFury card) { diff --git a/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java b/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java index e229e08cb0f..d232cccae20 100644 --- a/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java +++ b/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java @@ -36,9 +36,7 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -50,21 +48,12 @@ import mage.target.TargetPermanent; */ public class FiligreeFracture extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public FiligreeFracture(UUID ownerId) { super(ownerId, 82, "Filigree Fracture", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{G}"); this.expansionSetCode = "CON"; - // Destroy target artifact or enchantment. If that permanent was blue or black, draw a card. - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new FiligreeFractureEffect()); } diff --git a/Mage.Sets/src/mage/sets/darkascension/TorchFiend.java b/Mage.Sets/src/mage/sets/darkascension/TorchFiend.java index 662c0104c1f..285ab2610f7 100644 --- a/Mage.Sets/src/mage/sets/darkascension/TorchFiend.java +++ b/Mage.Sets/src/mage/sets/darkascension/TorchFiend.java @@ -28,7 +28,6 @@ package mage.sets.darkascension; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; diff --git a/Mage.Sets/src/mage/sets/darkascension/WerewolfRansacker.java b/Mage.Sets/src/mage/sets/darkascension/WerewolfRansacker.java index 452b74d0faa..20b2619b740 100644 --- a/Mage.Sets/src/mage/sets/darkascension/WerewolfRansacker.java +++ b/Mage.Sets/src/mage/sets/darkascension/WerewolfRansacker.java @@ -28,7 +28,6 @@ package mage.sets.darkascension; import java.util.UUID; - import mage.constants.*; import mage.MageInt; import mage.abilities.Ability; @@ -45,6 +44,7 @@ import mage.abilities.keyword.TransformAbility; import mage.cards.CardImpl; import mage.filter.FilterPermanent; import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -90,17 +90,12 @@ public class WerewolfRansacker extends CardImpl { } class WerewolfRansackerAbility extends TriggeredAbilityImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact"); + public static final String RULE_TEXT = "Whenever this creature transforms into Werewolf Ransacker, you may destroy target artifact. If that artifact is put into a graveyard this way, Werewolf Ransacker deals 3 damage to that artifact's controller"; - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - + public WerewolfRansackerAbility() { super(Zone.BATTLEFIELD, new WerewolfRansackerEffect(), true); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactPermanent()); this.addTarget(target); } @@ -129,7 +124,6 @@ class WerewolfRansackerAbility extends TriggeredAbilityImpl { public String getRule() { return RULE_TEXT + '.'; } - } class WerewolfRansackerEffect extends OneShotEffect { @@ -167,5 +161,4 @@ class WerewolfRansackerEffect extends OneShotEffect { } return affectedTargets > 0; } - } diff --git a/Mage.Sets/src/mage/sets/darksteel/Oxidize.java b/Mage.Sets/src/mage/sets/darksteel/Oxidize.java index a0d075f0923..0cae108e86f 100644 --- a/Mage.Sets/src/mage/sets/darksteel/Oxidize.java +++ b/Mage.Sets/src/mage/sets/darksteel/Oxidize.java @@ -25,17 +25,14 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.darksteel; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -43,18 +40,12 @@ import mage.target.TargetPermanent; */ public class Oxidize extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Oxidize(UUID ownerId) { super(ownerId, 79, "Oxidize", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{G}"); this.expansionSetCode = "DST"; this.getSpellAbility().addEffect(new DestroyTargetEffect(true)); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); } public Oxidize(final Oxidize card) { @@ -65,5 +56,4 @@ public class Oxidize extends CardImpl { public Oxidize copy() { return new Oxidize(this); } - } diff --git a/Mage.Sets/src/mage/sets/darksteel/ViridianZealot.java b/Mage.Sets/src/mage/sets/darksteel/ViridianZealot.java index ba3372745f9..d8e1ace7d80 100644 --- a/Mage.Sets/src/mage/sets/darksteel/ViridianZealot.java +++ b/Mage.Sets/src/mage/sets/darksteel/ViridianZealot.java @@ -25,11 +25,9 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.darksteel; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; @@ -40,9 +38,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -50,14 +46,6 @@ import mage.target.TargetPermanent; */ public class ViridianZealot extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public ViridianZealot(UUID ownerId) { super(ownerId, 90, "Viridian Zealot", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{G}{G}"); this.expansionSetCode = "DST"; @@ -66,9 +54,11 @@ public class ViridianZealot extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(1); + + // {1}{G}, Sacrifice Viridian Zealot: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{1}{G}")); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } @@ -80,5 +70,4 @@ public class ViridianZealot extends CardImpl { public ViridianZealot copy() { return new ViridianZealot(this); } - } diff --git a/Mage.Sets/src/mage/sets/dissension/IndrikStomphowler.java b/Mage.Sets/src/mage/sets/dissension/IndrikStomphowler.java index ab3cfcce029..0181c37f9e2 100644 --- a/Mage.Sets/src/mage/sets/dissension/IndrikStomphowler.java +++ b/Mage.Sets/src/mage/sets/dissension/IndrikStomphowler.java @@ -28,7 +28,6 @@ package mage.sets.dissension; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; @@ -36,9 +35,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -46,14 +43,6 @@ import mage.target.TargetPermanent; */ public class IndrikStomphowler extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public IndrikStomphowler(UUID ownerId) { super(ownerId, 86, "Indrik Stomphowler", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{4}{G}"); this.expansionSetCode = "DIS"; @@ -62,7 +51,7 @@ public class IndrikStomphowler extends CardImpl { this.power = new MageInt(4); this.toughness = new MageInt(4); Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/fifthdawn/TelJiladJustice.java b/Mage.Sets/src/mage/sets/fifthdawn/TelJiladJustice.java index 37cfafa7776..5e09b49285b 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/TelJiladJustice.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/TelJiladJustice.java @@ -45,7 +45,6 @@ public class TelJiladJustice extends CardImpl { super(ownerId, 95, "Tel-Jilad Justice", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{G}"); this.expansionSetCode = "5DN"; - // Destroy target artifact. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetArtifactPermanent()); diff --git a/Mage.Sets/src/mage/sets/guildpact/ShatteringSpree.java b/Mage.Sets/src/mage/sets/guildpact/ShatteringSpree.java index 8d445861e8d..f84eca751d1 100644 --- a/Mage.Sets/src/mage/sets/guildpact/ShatteringSpree.java +++ b/Mage.Sets/src/mage/sets/guildpact/ShatteringSpree.java @@ -46,7 +46,6 @@ public class ShatteringSpree extends CardImpl { super(ownerId, 75, "Shattering Spree", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{R}"); this.expansionSetCode = "GPT"; - // Replicate {R} this.addAbility(new ReplicateAbility(this, "{R}")); // Destroy target artifact. diff --git a/Mage.Sets/src/mage/sets/innistrad/AncientGrudge.java b/Mage.Sets/src/mage/sets/innistrad/AncientGrudge.java index 8936c752c6b..54a7f3404ef 100644 --- a/Mage.Sets/src/mage/sets/innistrad/AncientGrudge.java +++ b/Mage.Sets/src/mage/sets/innistrad/AncientGrudge.java @@ -34,8 +34,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.FlashbackAbility; import mage.cards.CardImpl; import mage.constants.TimingRule; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; import java.util.UUID; @@ -44,21 +43,13 @@ import java.util.UUID; * @author nantuko */ public class AncientGrudge extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public AncientGrudge(UUID ownerId) { super(ownerId, 127, "Ancient Grudge", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}"); this.expansionSetCode = "ISD"; - // Destroy target artifact. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); // Flashback {G} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{G}"), TimingRule.INSTANT)); diff --git a/Mage.Sets/src/mage/sets/invasion/DismantlingBlow.java b/Mage.Sets/src/mage/sets/invasion/DismantlingBlow.java index 7d6e4f246f9..7d3e1c221d3 100644 --- a/Mage.Sets/src/mage/sets/invasion/DismantlingBlow.java +++ b/Mage.Sets/src/mage/sets/invasion/DismantlingBlow.java @@ -36,9 +36,7 @@ import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -47,24 +45,15 @@ import mage.target.TargetPermanent; */ public class DismantlingBlow extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public DismantlingBlow(UUID ownerId) { super(ownerId, 14, "Dismantling Blow", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{W}"); this.expansionSetCode = "INV"; - // Kicker {2}{U} this.addAbility(new KickerAbility("{2}{U}")); // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); // If Dismantling Blow was kicked, draw two cards. this.getSpellAbility().addEffect(new ConditionalOneShotEffect( new DrawCardSourceControllerEffect(2), diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/SultaiCharm.java b/Mage.Sets/src/mage/sets/khansoftarkir/SultaiCharm.java index c81bb8869c1..68e6d6f4450 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/SultaiCharm.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/SultaiCharm.java @@ -57,7 +57,6 @@ public class SultaiCharm extends CardImpl { super(ownerId, 204, "Sultai Charm", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{B}{G}{U}"); this.expansionSetCode = "KTK"; - // Choose one - // * Destroy target monocolored creature. this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); @@ -73,7 +72,6 @@ public class SultaiCharm extends CardImpl { mode = new Mode(); mode.getEffects().add(new DrawDiscardControllerEffect(2,1)); this.getSpellAbility().addMode(mode); - } public SultaiCharm(final SultaiCharm card) { diff --git a/Mage.Sets/src/mage/sets/magic2010/SolemnOffering.java b/Mage.Sets/src/mage/sets/magic2010/SolemnOffering.java index f3d3f8deb3b..2c695654c36 100644 --- a/Mage.Sets/src/mage/sets/magic2010/SolemnOffering.java +++ b/Mage.Sets/src/mage/sets/magic2010/SolemnOffering.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.magic2010; import java.util.UUID; @@ -34,9 +33,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -45,19 +42,12 @@ import mage.target.TargetPermanent; */ public class SolemnOffering extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public SolemnOffering(UUID ownerId) { super(ownerId, 33, "Solemn Offering", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{2}{W}"); this.expansionSetCode = "M10"; - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + // Destroy target artifact or enchantment. You gain 4 life. + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new GainLifeEffect(4)); } diff --git a/Mage.Sets/src/mage/sets/magic2011/ManicVandal.java b/Mage.Sets/src/mage/sets/magic2011/ManicVandal.java index b4974d67244..c00248da50d 100644 --- a/Mage.Sets/src/mage/sets/magic2011/ManicVandal.java +++ b/Mage.Sets/src/mage/sets/magic2011/ManicVandal.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.magic2011; import java.util.UUID; @@ -36,8 +35,7 @@ import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.Target; import mage.target.TargetPermanent; @@ -47,12 +45,6 @@ import mage.target.TargetPermanent; */ public class ManicVandal extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public ManicVandal(UUID ownerId) { super(ownerId, 151, "Manic Vandal", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{R}"); this.expansionSetCode = "M11"; @@ -63,7 +55,7 @@ public class ManicVandal extends CardImpl { this.toughness = new MageInt(2); Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactPermanent()); ability.addTarget(target); this.addAbility(ability); } @@ -76,5 +68,4 @@ public class ManicVandal extends CardImpl { public ManicVandal copy() { return new ManicVandal(this); } - } diff --git a/Mage.Sets/src/mage/sets/magic2013/Smelt.java b/Mage.Sets/src/mage/sets/magic2013/Smelt.java index ff67773bb90..2db77343702 100644 --- a/Mage.Sets/src/mage/sets/magic2013/Smelt.java +++ b/Mage.Sets/src/mage/sets/magic2013/Smelt.java @@ -44,7 +44,6 @@ public class Smelt extends CardImpl { super(ownerId, 149, "Smelt", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}"); this.expansionSetCode = "M13"; - // Destroy target artifact. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addTarget(new TargetArtifactPermanent()); diff --git a/Mage.Sets/src/mage/sets/mirrodin/Shatter.java b/Mage.Sets/src/mage/sets/mirrodin/Shatter.java index 44bce28e959..6c8c5d51543 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/Shatter.java +++ b/Mage.Sets/src/mage/sets/mirrodin/Shatter.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.mirrodin; import java.util.UUID; @@ -33,8 +32,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -42,19 +40,13 @@ import mage.target.TargetPermanent; * @author Loki */ public class Shatter extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Shatter (UUID ownerId) { super(ownerId, 105, "Shatter", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}"); this.expansionSetCode = "MRD"; // Destroy target artifact. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); } public Shatter (final Shatter card) { @@ -65,5 +57,4 @@ public class Shatter extends CardImpl { public Shatter copy() { return new Shatter(this); } - } diff --git a/Mage.Sets/src/mage/sets/mirrodinbesieged/ViridianCorrupter.java b/Mage.Sets/src/mage/sets/mirrodinbesieged/ViridianCorrupter.java index 93d5b5a96b7..1b78b3a011c 100644 --- a/Mage.Sets/src/mage/sets/mirrodinbesieged/ViridianCorrupter.java +++ b/Mage.Sets/src/mage/sets/mirrodinbesieged/ViridianCorrupter.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.mirrodinbesieged; import java.util.UUID; @@ -37,8 +36,7 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.InfectAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.Target; import mage.target.TargetPermanent; @@ -48,12 +46,6 @@ import mage.target.TargetPermanent; */ public class ViridianCorrupter extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public ViridianCorrupter (UUID ownerId) { super(ownerId, 94, "Viridian Corrupter", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); this.expansionSetCode = "MBS"; @@ -64,8 +56,9 @@ public class ViridianCorrupter extends CardImpl { this.toughness = new MageInt(2); this.addAbility(InfectAbility.getInstance()); + // When Viridian Corrupter enters the battlefield, destroy target artifact. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactPermanent()); ability.addTarget(target); this.addAbility(ability); } @@ -78,5 +71,4 @@ public class ViridianCorrupter extends CardImpl { public ViridianCorrupter copy() { return new ViridianCorrupter(this); } - } diff --git a/Mage.Sets/src/mage/sets/nemesis/SealOfCleansing.java b/Mage.Sets/src/mage/sets/nemesis/SealOfCleansing.java index b4ba95f23ff..19f87fb040b 100644 --- a/Mage.Sets/src/mage/sets/nemesis/SealOfCleansing.java +++ b/Mage.Sets/src/mage/sets/nemesis/SealOfCleansing.java @@ -28,7 +28,6 @@ package mage.sets.nemesis; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.Ability; @@ -37,9 +36,7 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -47,23 +44,14 @@ import mage.target.TargetPermanent; * @author Plopman */ public class SealOfCleansing extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public SealOfCleansing(UUID ownerId) { super(ownerId, 18, "Seal of Cleansing", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}"); this.expansionSetCode = "NMS"; - // Sacrifice Seal of Cleansing: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/odyssey/RayOfDistortion.java b/Mage.Sets/src/mage/sets/odyssey/RayOfDistortion.java index 36233bea58c..a57877773cf 100644 --- a/Mage.Sets/src/mage/sets/odyssey/RayOfDistortion.java +++ b/Mage.Sets/src/mage/sets/odyssey/RayOfDistortion.java @@ -35,9 +35,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.TimingRule; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -46,20 +44,13 @@ import mage.target.TargetPermanent; */ public class RayOfDistortion extends CardImpl { - static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); - } - public RayOfDistortion(UUID ownerId) { super(ownerId, 42, "Ray of Distortion", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{3}{W}"); this.expansionSetCode = "ODY"; - // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); // Flashback {4}{W}{W} this.addAbility(new FlashbackAbility(new ManaCostsImpl("{4}{W}{W}"), TimingRule.INSTANT)); } diff --git a/Mage.Sets/src/mage/sets/planarchaos/SealOfPrimordium.java b/Mage.Sets/src/mage/sets/planarchaos/SealOfPrimordium.java index 91d38aa731d..4dcee2ae1b4 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/SealOfPrimordium.java +++ b/Mage.Sets/src/mage/sets/planarchaos/SealOfPrimordium.java @@ -28,7 +28,6 @@ package mage.sets.planarchaos; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.Ability; @@ -37,9 +36,7 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -48,22 +45,13 @@ import mage.target.TargetPermanent; */ public class SealOfPrimordium extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public SealOfPrimordium(UUID ownerId) { super(ownerId, 153, "Seal of Primordium", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}"); this.expansionSetCode = "PLC"; - // Sacrifice Seal of Primordium: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/planechase2012/NullmageAdvocate.java b/Mage.Sets/src/mage/sets/planechase2012/NullmageAdvocate.java index 6d89ed28723..358c04d9e5f 100644 --- a/Mage.Sets/src/mage/sets/planechase2012/NullmageAdvocate.java +++ b/Mage.Sets/src/mage/sets/planechase2012/NullmageAdvocate.java @@ -40,9 +40,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.filter.FilterCard; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; import mage.target.common.TargetCardInOpponentsGraveyard; import mage.target.targetpointer.SecondTargetPointer; @@ -53,17 +51,11 @@ import mage.target.targetpointer.SecondTargetPointer; */ public class NullmageAdvocate extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent(); - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); - } - public NullmageAdvocate(UUID ownerId) { super(ownerId, 70, "Nullmage Advocate", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{G}"); this.expansionSetCode = "PC2"; this.subtype.add("Insect"); this.subtype.add("Druid"); - this.power = new MageInt(2); this.toughness = new MageInt(3); @@ -76,7 +68,7 @@ public class NullmageAdvocate extends CardImpl { effect.setTargetPointer(new SecondTargetPointer()); ability.addEffect(effect); ability.addTarget(new TargetCardInOpponentsGraveyard(2,2, new FilterCard("two target cards from an opponent's graveyard"), true)); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/ravnica/NullmageShepherd.java b/Mage.Sets/src/mage/sets/ravnica/NullmageShepherd.java index ede068e74eb..160b53f7c57 100644 --- a/Mage.Sets/src/mage/sets/ravnica/NullmageShepherd.java +++ b/Mage.Sets/src/mage/sets/ravnica/NullmageShepherd.java @@ -28,7 +28,6 @@ package mage.sets.ravnica; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; @@ -38,10 +37,9 @@ import mage.abilities.costs.common.TapTargetCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Zone; -import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.filter.predicate.permanent.TappedPredicate; import mage.target.TargetPermanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -53,13 +51,8 @@ import mage.target.common.TargetControlledCreaturePermanent; public class NullmageShepherd extends CardImpl { private static final FilterControlledCreaturePermanent filterCost = new FilterControlledCreaturePermanent("untapped creatures you control"); - private static final FilterPermanent filterTarget = new FilterPermanent("artifact or enchantment"); - static { filterCost.add(Predicates.not(new TappedPredicate())); - filterTarget.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); } public NullmageShepherd(UUID ownerId) { @@ -73,7 +66,7 @@ public class NullmageShepherd extends CardImpl { // Tap four untapped creatures you control: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new TapTargetCost(new TargetControlledCreaturePermanent(4, 4, filterCost, true))); - ability.addTarget(new TargetPermanent(filterTarget)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/ravnica/SunderingVitae.java b/Mage.Sets/src/mage/sets/ravnica/SunderingVitae.java index bf5465aa574..208e25aa391 100644 --- a/Mage.Sets/src/mage/sets/ravnica/SunderingVitae.java +++ b/Mage.Sets/src/mage/sets/ravnica/SunderingVitae.java @@ -33,9 +33,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.ConvokeAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -43,24 +41,15 @@ import mage.target.TargetPermanent; * @author jonubuu */ public class SunderingVitae extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public SunderingVitae(UUID ownerId) { super(ownerId, 185, "Sundering Vitae", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{G}"); this.expansionSetCode = "RAV"; - // Convoke this.addAbility(new ConvokeAbility()); // Destroy target artifact or enchantment. - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/RakdosCharm.java b/Mage.Sets/src/mage/sets/returntoravnica/RakdosCharm.java index d6b4a5f73fe..5ad478abf5c 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/RakdosCharm.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/RakdosCharm.java @@ -55,7 +55,6 @@ public class RakdosCharm extends CardImpl { super(ownerId, 184, "Rakdos Charm", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{B}{R}"); this.expansionSetCode = "RTR"; - // Choose one — Exile all cards from target player's graveyard; this.getSpellAbility().addEffect(new ExileGraveyardAllTargetPlayerEffect()); this.getSpellAbility().addTarget(new TargetPlayer()); @@ -81,7 +80,6 @@ public class RakdosCharm extends CardImpl { return new RakdosCharm(this); } - private class RakdosCharmDamageEffect extends OneShotEffect { public RakdosCharmDamageEffect() { @@ -113,6 +111,5 @@ public class RakdosCharm extends CardImpl { public RakdosCharmDamageEffect copy() { return new RakdosCharmDamageEffect(this); } - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/RendingVines.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/RendingVines.java index 4e59199b067..0716462a951 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/RendingVines.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/RendingVines.java @@ -52,7 +52,6 @@ public class RendingVines extends CardImpl { this.expansionSetCode = "SOK"; this.subtype.add("Arcane"); - // Destroy target artifact or enchantment if its converted mana cost is less than or equal to the number of cards in your hand. this.getSpellAbility().addEffect(new RendingVinesEffect()); this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/YukiOnna.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/YukiOnna.java index 8c9f6a80da0..3c63718db89 100644 --- a/Mage.Sets/src/mage/sets/saviorsofkamigawa/YukiOnna.java +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/YukiOnna.java @@ -28,7 +28,6 @@ package mage.sets.saviorsofkamigawa; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; @@ -38,9 +37,8 @@ import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; import mage.filter.common.FilterSpiritOrArcaneCard; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -48,13 +46,6 @@ import mage.target.TargetPermanent; */ public class YukiOnna extends CardImpl { - private static final FilterSpiritOrArcaneCard filter = new FilterSpiritOrArcaneCard(); - private static final FilterPermanent filterTarget = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public YukiOnna(UUID ownerId) { super(ownerId, 120, "Yuki-Onna", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{R}"); this.expansionSetCode = "SOK"; @@ -65,10 +56,10 @@ public class YukiOnna extends CardImpl { // When Yuki-Onna enters the battlefield, destroy target artifact. Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - ability.addTarget(new TargetPermanent(filterTarget)); + ability.addTarget(new TargetPermanent(new FilterArtifactPermanent())); this.addAbility(ability); // Whenever you cast a Spirit or Arcane spell, you may return Yuki-Onna to its owner's hand. - this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), filter, true)); + this.addAbility(new SpellCastControllerTriggeredAbility(new ReturnToHandSourceEffect(true), new FilterSpiritOrArcaneCard(), true)); } public YukiOnna(final YukiOnna card) { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/SliceinTwain.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/SliceinTwain.java index a66f44aa3fa..668c02f32e0 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/SliceinTwain.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/SliceinTwain.java @@ -34,9 +34,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -44,13 +42,6 @@ import mage.target.TargetPermanent; * @author Loki */ public class SliceinTwain extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public SliceinTwain (UUID ownerId) { super(ownerId, 127, "Slice in Twain", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{G}{G}"); @@ -58,7 +49,7 @@ public class SliceinTwain extends CardImpl { this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); } public SliceinTwain (final SliceinTwain card) { @@ -69,5 +60,4 @@ public class SliceinTwain extends CardImpl { public SliceinTwain copy() { return new SliceinTwain(this); } - } diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/SylvokReplica.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/SylvokReplica.java index d4f6062842b..20a3e9bd36a 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/SylvokReplica.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/SylvokReplica.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.scarsofmirrodin; import java.util.UUID; @@ -39,9 +38,7 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -49,13 +46,6 @@ import mage.target.TargetPermanent; * @author Loki */ public class SylvokReplica extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public SylvokReplica (UUID ownerId) { super(ownerId, 210, "Sylvok Replica", Rarity.COMMON, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}"); @@ -63,9 +53,11 @@ public class SylvokReplica extends CardImpl { this.subtype.add("Shaman"); this.power = new MageInt(1); this.toughness = new MageInt(3); + + // {G}, Sacrifice Sylvok Replica: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{G}")); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } @@ -77,5 +69,4 @@ public class SylvokReplica extends CardImpl { public SylvokReplica copy() { return new SylvokReplica(this); } - } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java b/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java index e2799927f80..16869be8383 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/GleefulSabotage.java @@ -33,9 +33,7 @@ import mage.abilities.keyword.ConspireAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -44,25 +42,16 @@ import mage.target.TargetPermanent; */ public class GleefulSabotage extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("target artifact or enchantment"); - - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), - (new CardTypePredicate(CardType.ENCHANTMENT)))); - } - public GleefulSabotage(UUID ownerId) { super(ownerId, 116, "Gleeful Sabotage", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{1}{G}"); this.expansionSetCode = "SHM"; - // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); // Conspire this.addAbility(new ConspireAbility(this)); - } public GleefulSabotage(final GleefulSabotage card) { @@ -74,4 +63,3 @@ public class GleefulSabotage extends CardImpl { return new GleefulSabotage(this); } } - diff --git a/Mage.Sets/src/mage/sets/shadowmoor/SmashToSmithereens.java b/Mage.Sets/src/mage/sets/shadowmoor/SmashToSmithereens.java index 3214585d64f..98ff75dc2bc 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/SmashToSmithereens.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/SmashToSmithereens.java @@ -51,7 +51,6 @@ public class SmashToSmithereens extends CardImpl { super(ownerId, 107, "Smash to Smithereens", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}"); this.expansionSetCode = "SHM"; - // Destroy target artifact. Smash to Smithereens deals 3 damage to that artifact's controller. this.getSpellAbility().addTarget(new TargetArtifactPermanent()); this.getSpellAbility().addEffect(new DestroyTargetEffect()); diff --git a/Mage.Sets/src/mage/sets/shardsofalara/DispellersCapsule.java b/Mage.Sets/src/mage/sets/shardsofalara/DispellersCapsule.java index 29d1688d48d..74a8c8d8471 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/DispellersCapsule.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/DispellersCapsule.java @@ -25,11 +25,9 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.shardsofalara; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; @@ -39,9 +37,7 @@ import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -49,21 +45,14 @@ import mage.target.TargetPermanent; * @author Loki */ public class DispellersCapsule extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - + public DispellersCapsule (UUID ownerId) { super(ownerId, 8, "Dispeller's Capsule", Rarity.COMMON, new CardType[]{CardType.ARTIFACT}, "{W}"); this.expansionSetCode = "ALA"; Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{2}{W}")); ability.addCost(new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/tempest/Disenchant.java b/Mage.Sets/src/mage/sets/tempest/Disenchant.java index 1a217e67c26..2d65f90508b 100644 --- a/Mage.Sets/src/mage/sets/tempest/Disenchant.java +++ b/Mage.Sets/src/mage/sets/tempest/Disenchant.java @@ -32,9 +32,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -43,20 +41,13 @@ import mage.target.TargetPermanent; */ public class Disenchant extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public Disenchant(UUID ownerId) { super(ownerId, 228, "Disenchant", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{W}"); this.expansionSetCode = "TMP"; + // Destroy target artifact or enchantment. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); } public Disenchant(final Disenchant card) { diff --git a/Mage.Sets/src/mage/sets/tempest/Verdigris.java b/Mage.Sets/src/mage/sets/tempest/Verdigris.java index dbcf4169c3d..d0ceab23f2f 100644 --- a/Mage.Sets/src/mage/sets/tempest/Verdigris.java +++ b/Mage.Sets/src/mage/sets/tempest/Verdigris.java @@ -32,8 +32,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -42,18 +41,12 @@ import mage.target.TargetPermanent; */ public class Verdigris extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Verdigris(UUID ownerId) { super(ownerId, 158, "Verdigris", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{G}"); this.expansionSetCode = "TMP"; this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); } public Verdigris(final Verdigris card) { diff --git a/Mage.Sets/src/mage/sets/tenthedition/Naturalize.java b/Mage.Sets/src/mage/sets/tenthedition/Naturalize.java index 3476eec9d73..7caac8fd17b 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/Naturalize.java +++ b/Mage.Sets/src/mage/sets/tenthedition/Naturalize.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.tenthedition; import java.util.UUID; @@ -33,9 +32,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -44,19 +41,12 @@ import mage.target.TargetPermanent; */ public class Naturalize extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public Naturalize(UUID ownerId){ super(ownerId, 282, "Naturalize", Rarity.COMMON, new CardType[]{CardType.INSTANT},"{1}{G}"); this.expansionSetCode = "10E"; - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + // Destroy target artifact or enchantment. + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/sets/theros/ArtisansSorrow.java b/Mage.Sets/src/mage/sets/theros/ArtisansSorrow.java index 650dfe91f86..c24bc93531d 100644 --- a/Mage.Sets/src/mage/sets/theros/ArtisansSorrow.java +++ b/Mage.Sets/src/mage/sets/theros/ArtisansSorrow.java @@ -33,9 +33,7 @@ import mage.abilities.effects.keyword.ScryEffect; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -43,20 +41,13 @@ import mage.target.TargetPermanent; * @author LevelX2 */ public class ArtisansSorrow extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); - } - public ArtisansSorrow(UUID ownerId) { super(ownerId, 151, "Artisan's Sorrow", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{3}{G}"); this.expansionSetCode = "THS"; - // Destroy target artifact or enchantment. Scry 2. this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new ScryEffect(2)); } diff --git a/Mage.Sets/src/mage/sets/theros/DestructiveRevelry.java b/Mage.Sets/src/mage/sets/theros/DestructiveRevelry.java index 9bc8aeda091..88cb8a54b53 100644 --- a/Mage.Sets/src/mage/sets/theros/DestructiveRevelry.java +++ b/Mage.Sets/src/mage/sets/theros/DestructiveRevelry.java @@ -34,9 +34,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -49,19 +47,13 @@ import mage.target.TargetPermanent; */ public class DestructiveRevelry extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - static { - filter.add(Predicates.or(new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ENCHANTMENT))); - } - public DestructiveRevelry(UUID ownerId) { super(ownerId, 192, "Destructive Revelry", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{R}{G}"); this.expansionSetCode = "THS"; - // Destroy target artifact or enchantment. Destructive Revelry deals 2 damage to that permanent's controller. this.getSpellAbility().addEffect(new DestructiveRevelryEffect()); - Target target = new TargetPermanent(filter); + Target target = new TargetPermanent(new FilterArtifactOrEnchantmentPermanent()); this.getSpellAbility().addTarget(target); } diff --git a/Mage.Sets/src/mage/sets/timespiral/HarmonicSliver.java b/Mage.Sets/src/mage/sets/timespiral/HarmonicSliver.java index fef56e3baa3..bfee4df16fd 100644 --- a/Mage.Sets/src/mage/sets/timespiral/HarmonicSliver.java +++ b/Mage.Sets/src/mage/sets/timespiral/HarmonicSliver.java @@ -39,10 +39,8 @@ import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.continuous.GainAbilityAllEffect; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.filter.predicate.mageobject.SubtypePredicate; import mage.target.TargetPermanent; @@ -53,13 +51,8 @@ import mage.target.TargetPermanent; public class HarmonicSliver extends CardImpl { private static final FilterCreaturePermanent filterSliver = new FilterCreaturePermanent(); - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); static { filterSliver.add(new SubtypePredicate("Sliver")); - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public HarmonicSliver(UUID ownerId) { @@ -72,7 +65,7 @@ public class HarmonicSliver extends CardImpl { // All Slivers have "When this permanent enters the battlefield, destroy target artifact or enchantment." Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - TargetPermanent target = new TargetPermanent(filter); + TargetPermanent target = new TargetPermanent(new FilterArtifactOrEnchantmentPermanent()); ability.addTarget(target); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect( ability, Duration.WhileOnBattlefield, diff --git a/Mage.Sets/src/mage/sets/timespiral/KrosanGrip.java b/Mage.Sets/src/mage/sets/timespiral/KrosanGrip.java index 8c434a31c1b..9be46756220 100644 --- a/Mage.Sets/src/mage/sets/timespiral/KrosanGrip.java +++ b/Mage.Sets/src/mage/sets/timespiral/KrosanGrip.java @@ -33,9 +33,7 @@ import mage.constants.Rarity; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.SplitSecondAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -43,24 +41,15 @@ import mage.target.TargetPermanent; * @author jonubuu */ public class KrosanGrip extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public KrosanGrip(UUID ownerId) { super(ownerId, 202, "Krosan Grip", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{G}"); this.expansionSetCode = "TSP"; - // Split second this.addAbility(new SplitSecondAbility()); // Destroy target artifact or enchantment. - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/sets/urzaslegacy/ViashinoHeretic.java b/Mage.Sets/src/mage/sets/urzaslegacy/ViashinoHeretic.java index 4572ec8cfa7..dc603948e84 100644 --- a/Mage.Sets/src/mage/sets/urzaslegacy/ViashinoHeretic.java +++ b/Mage.Sets/src/mage/sets/urzaslegacy/ViashinoHeretic.java @@ -114,5 +114,4 @@ class ViashinoHereticEffect extends OneShotEffect { public String getText(Mode mode) { return "Destroy target artifact. Viashino Heretic deals damage to that artifact's controller equal to the artifact's converted mana cost"; } - } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/urzassaga/Scrap.java b/Mage.Sets/src/mage/sets/urzassaga/Scrap.java index 3d98daa3a90..de35dc13455 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Scrap.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Scrap.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.urzassaga; import java.util.UUID; @@ -35,8 +34,7 @@ import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactPermanent; import mage.target.TargetPermanent; /** @@ -45,18 +43,12 @@ import mage.target.TargetPermanent; */ public class Scrap extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact"); - - static { - filter.add(new CardTypePredicate(CardType.ARTIFACT)); - } - public Scrap(UUID ownerId) { super(ownerId, 213, "Scrap", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{R}"); this.expansionSetCode = "USG"; this.getSpellAbility().addEffect(new DestroyTargetEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); } diff --git a/Mage.Sets/src/mage/sets/vintagemasters/DevoutWitness.java b/Mage.Sets/src/mage/sets/vintagemasters/DevoutWitness.java index 56b9bf4d682..e3b050e15d6 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/DevoutWitness.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/DevoutWitness.java @@ -39,9 +39,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -50,14 +48,6 @@ import mage.target.TargetPermanent; */ public class DevoutWitness extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public DevoutWitness(UUID ownerId) { super(ownerId, 24, "Devout Witness", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{W}"); this.expansionSetCode = "VMA"; @@ -71,7 +61,7 @@ public class DevoutWitness extends CardImpl { Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{1}{W}")); ability.addCost(new TapSourceCost()); ability.addCost(new DiscardCardCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/weatherlight/AuraOfSilence.java b/Mage.Sets/src/mage/sets/weatherlight/AuraOfSilence.java index 66627a149a1..a6a93384e85 100644 --- a/Mage.Sets/src/mage/sets/weatherlight/AuraOfSilence.java +++ b/Mage.Sets/src/mage/sets/weatherlight/AuraOfSilence.java @@ -43,9 +43,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.target.TargetPermanent; import mage.util.CardUtil; @@ -55,25 +53,16 @@ import mage.util.CardUtil; * @author emerald000 */ public class AuraOfSilence extends CardImpl { - - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } - public AuraOfSilence(UUID ownerId) { super(ownerId, 123, "Aura of Silence", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}"); this.expansionSetCode = "WTH"; - // Artifact and enchantment spells your opponents cast cost {2} more to cast. this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new AuraOfSilenceCostModificationEffect())); // Sacrifice Aura of Silence: Destroy target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost()); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/worldwake/NaturesClaim.java b/Mage.Sets/src/mage/sets/worldwake/NaturesClaim.java index 08fcea4fdfd..8bfc053fdf2 100644 --- a/Mage.Sets/src/mage/sets/worldwake/NaturesClaim.java +++ b/Mage.Sets/src/mage/sets/worldwake/NaturesClaim.java @@ -25,11 +25,9 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.worldwake; import java.util.UUID; - import mage.constants.CardType; import mage.constants.Rarity; import mage.abilities.Ability; @@ -38,9 +36,7 @@ import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.constants.Outcome; import mage.constants.Zone; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.players.Player; @@ -51,21 +47,15 @@ import mage.target.TargetPermanent; * @author Loki */ public class NaturesClaim extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public NaturesClaim (UUID ownerId) { super(ownerId, 108, "Nature's Claim", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{G}"); this.expansionSetCode = "WWK"; + // Destroy target artifact or enchantment. Its controller gains 4 life. this.getSpellAbility().addEffect(new DestroyTargetEffect()); this.getSpellAbility().addEffect(new NaturesClaimEffect()); - this.getSpellAbility().addTarget(new TargetPermanent(filter)); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); } public NaturesClaim (final NaturesClaim card) { @@ -105,5 +95,4 @@ class NaturesClaimEffect extends OneShotEffect { public NaturesClaimEffect copy() { return new NaturesClaimEffect(this); } - } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/zendikar/KorSanctifiers.java b/Mage.Sets/src/mage/sets/zendikar/KorSanctifiers.java index 938a174c885..ef0d2c637cd 100644 --- a/Mage.Sets/src/mage/sets/zendikar/KorSanctifiers.java +++ b/Mage.Sets/src/mage/sets/zendikar/KorSanctifiers.java @@ -25,7 +25,6 @@ * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. */ - package mage.sets.zendikar; import java.util.UUID; @@ -38,9 +37,7 @@ import mage.abilities.decorator.ConditionalTriggeredAbility; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.keyword.KickerAbility; import mage.cards.CardImpl; -import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.target.TargetPermanent; /** @@ -48,13 +45,6 @@ import mage.target.TargetPermanent; * @author Loki */ public class KorSanctifiers extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - new CardTypePredicate(CardType.ARTIFACT), - new CardTypePredicate(CardType.ENCHANTMENT))); - } public KorSanctifiers (UUID ownerId) { super(ownerId, 22, "Kor Sanctifiers", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{W}"); @@ -68,10 +58,9 @@ public class KorSanctifiers extends CardImpl { // Kicker {W} (You may pay an additional {W} as you cast this spell.) this.addAbility(new KickerAbility("{W}")); - // When Kor Sanctifiers enters the battlefield, if it was kicked, destroy target artifact or enchantment. EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false); - ability.addTarget(new TargetPermanent(filter)); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); this.addAbility(new ConditionalTriggeredAbility(ability, KickedCondition.getInstance(), "When {this} enters the battlefield, if it was kicked, destroy target artifact or enchantment.")); } @@ -83,5 +72,4 @@ public class KorSanctifiers extends CardImpl { public KorSanctifiers copy() { return new KorSanctifiers(this); } - } diff --git a/Mage.Sets/src/mage/sets/zendikar/RelicCrush.java b/Mage.Sets/src/mage/sets/zendikar/RelicCrush.java index 57f4342258e..150a3c4c73d 100644 --- a/Mage.Sets/src/mage/sets/zendikar/RelicCrush.java +++ b/Mage.Sets/src/mage/sets/zendikar/RelicCrush.java @@ -35,8 +35,7 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.filter.FilterPermanent; -import mage.filter.predicate.Predicates; -import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.TargetPermanent; @@ -47,24 +46,15 @@ import mage.target.TargetPermanent; */ public class RelicCrush extends CardImpl { - private static final FilterPermanent filter = new FilterPermanent("artifact or enchantment"); - - static { - filter.add(Predicates.or( - (new CardTypePredicate(CardType.ARTIFACT)), - (new CardTypePredicate(CardType.ENCHANTMENT)))); - } - public RelicCrush(UUID ownerId) { super(ownerId, 179, "Relic Crush", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{4}{G}"); this.expansionSetCode = "ZEN"; - // Destroy target artifact or enchantment and up to one other target artifact or enchantment. + FilterPermanent filter = new FilterArtifactOrEnchantmentPermanent(); this.getSpellAbility().addEffect(new RelicCrushEffect()); this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addTarget(new TargetPermanent(0, 1, filter, false)); - } public RelicCrush(final RelicCrush card) { diff --git a/Mage/src/mage/filter/common/FilterArtifactOrEnchantmentPermanent.java b/Mage/src/mage/filter/common/FilterArtifactOrEnchantmentPermanent.java index b65da88c931..7aabd0eeec5 100644 --- a/Mage/src/mage/filter/common/FilterArtifactOrEnchantmentPermanent.java +++ b/Mage/src/mage/filter/common/FilterArtifactOrEnchantmentPermanent.java @@ -1,9 +1,30 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * 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 mage.filter.common; import mage.constants.CardType; @@ -15,7 +36,6 @@ import mage.filter.predicate.mageobject.CardTypePredicate; * * @author LevelX2 */ - public class FilterArtifactOrEnchantmentPermanent extends FilterPermanent { public FilterArtifactOrEnchantmentPermanent() { From 58b0903dc4d5a57f3ad9c80ebb7d89278f4725c8 Mon Sep 17 00:00:00 2001 From: Zeplar Date: Fri, 5 Jun 2015 17:44:57 -0700 Subject: [PATCH 070/102] Added Dust Elemental. Modified "bounce a permanent" effect so it doesn't fizzle if too few targets are selected-- this bug only affected Dust Elemental and Stormfront Riders to my knowledge. --- .../mage/sets/planarchaos/DustElemental.java | 80 +++++++++++++++++++ .../sets/planarchaos/StormfrontRiders.java | 4 +- ...ToHandChosenControlledPermanentEffect.java | 15 +++- 3 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/planarchaos/DustElemental.java diff --git a/Mage.Sets/src/mage/sets/planarchaos/DustElemental.java b/Mage.Sets/src/mage/sets/planarchaos/DustElemental.java new file mode 100644 index 00000000000..34035273a96 --- /dev/null +++ b/Mage.Sets/src/mage/sets/planarchaos/DustElemental.java @@ -0,0 +1,80 @@ +/* + * 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 mage.sets.planarchaos; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.ReturnToHandChosenControlledPermanentEffect; +import mage.abilities.keyword.FlashAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.FearAbility; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.players.Player; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author Zeplar1_at_googlemail.com + */ +public class DustElemental extends CardImpl { + + private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(" creatures you control"); + + public DustElemental(UUID ownerId) { + super(ownerId, 5, "Dust Elemental", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{2}{W}{W}"); + this.expansionSetCode = "PLC"; + this.subtype.add("Elemental"); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + + // Flash + this.addAbility(FlashAbility.getInstance()); + // Flying; fear + this.addAbility(FlyingAbility.getInstance()); + this.addAbility(FearAbility.getInstance()); + // When Dust Elemental enters the battlefield, return three creatures you control to their owner's hand. + this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(filter,1,3))); + } + + public DustElemental(final DustElemental card) { + super(card); + } + + @Override + public DustElemental copy() { + return new DustElemental(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java b/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java index 13f78771209..e2415496f27 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java +++ b/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java @@ -39,7 +39,7 @@ import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.TargetController; import mage.constants.Zone; -import mage.filter.common.FilterControlledPermanent; +import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.other.OwnerPredicate; import mage.game.permanent.token.SoldierToken; @@ -68,7 +68,7 @@ public class StormfrontRiders extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Stormfront Riders enters the battlefield, return two creatures you control to their owner's hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(new FilterControlledPermanent("creatures you control"), 2))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(new FilterControlledCreaturePermanent("creatures you control"),1,2))); // Whenever Stormfront Riders or another creature is returned to your hand from the battlefield, put a 1/1 white Soldier creature token onto the battlefield. this.addAbility(new ZoneChangeAllTriggeredAbility(Zone.BATTLEFIELD, Zone.BATTLEFIELD, Zone.HAND, new CreateTokenEffect(new SoldierToken()), filter,"Whenever {this} or another creature is returned to your hand from the battlefield, ", false)); diff --git a/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java b/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java index c4e8b654eb9..a5aabf6310f 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java @@ -47,6 +47,7 @@ public class ReturnToHandChosenControlledPermanentEffect extends OneShotEffect { private final FilterControlledPermanent filter; private int number; + private int minNumber; public ReturnToHandChosenControlledPermanentEffect(FilterControlledPermanent filter) { this(filter, 1); @@ -55,6 +56,15 @@ public class ReturnToHandChosenControlledPermanentEffect extends OneShotEffect { super(Outcome.ReturnToHand); this.filter = filter; this.number = number; + this.minNumber = number; + this.staticText = getText(); + } + + public ReturnToHandChosenControlledPermanentEffect(FilterControlledPermanent filter, int minNumber, int maxNumber) { + super(Outcome.ReturnToHand); + this.filter = filter; + this.number = maxNumber; + this.minNumber = minNumber; this.staticText = getText(); } @@ -62,7 +72,10 @@ public class ReturnToHandChosenControlledPermanentEffect extends OneShotEffect { super(effect); this.filter = effect.filter; this.number = effect.number; + this.minNumber = this.minNumber; } + + @Override public ReturnToHandChosenControlledPermanentEffect copy() { @@ -73,7 +86,7 @@ public class ReturnToHandChosenControlledPermanentEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); if (player != null) { - TargetControlledPermanent target = new TargetControlledPermanent(number, number, filter, true); + TargetControlledPermanent target = new TargetControlledPermanent(minNumber, number, filter, true); if (player.choose(this.outcome, target, source.getSourceId(), game)) { for (UUID targetCreatureId : target.getTargets()) { Permanent permanent = game.getPermanent(targetCreatureId); From 80189f19af125d7bc7fb3ab91b6a81e3e32b13a6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 6 Jun 2015 20:46:42 +0200 Subject: [PATCH 071/102] * Apostle's Blessing - Fixed that choice of type of protection had wrongly to be done while going to stack instead during resolution. --- .../sets/newphyrexia/ApostlesBlessing.java | 63 +++++++++++-------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java b/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java index a040d8b982d..79bdbcafa4f 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/ApostlesBlessing.java @@ -34,17 +34,20 @@ import mage.constants.CardType; import mage.constants.Duration; import mage.constants.Rarity; import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.ProtectionAbility; import mage.cards.CardImpl; import mage.choices.ChoiceColorOrArtifact; +import mage.constants.Outcome; import mage.filter.FilterCard; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.filter.predicate.mageobject.ColorPredicate; import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.players.Player; import mage.target.common.TargetControlledPermanent; /** @@ -62,10 +65,11 @@ public class ApostlesBlessing extends CardImpl { public ApostlesBlessing(UUID ownerId) { super(ownerId, 2, "Apostle's Blessing", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{WP}"); this.expansionSetCode = "NPH"; - - this.getSpellAbility().addEffect(new ApostlesBlessingEffect(Duration.EndOfTurn)); + + // ({WP} can be paid with either {W} or 2 life.) + // Target artifact or creature you control gains protection from artifacts or from the color of your choice until end of turn. + this.getSpellAbility().addEffect(new ApostlesBlessingEffect()); this.getSpellAbility().addTarget(new TargetControlledPermanent(filter)); - this.getSpellAbility().addChoice(new ChoiceColorOrArtifact()); } public ApostlesBlessing(final ApostlesBlessing card) { @@ -79,40 +83,47 @@ public class ApostlesBlessing extends CardImpl { } -class ApostlesBlessingEffect extends GainAbilityTargetEffect { - - public ApostlesBlessingEffect(Duration duration) { - super(new ProtectionAbility(new FilterCard()), duration); - staticText = "Target artifact or creature gains protection from artifacts or from the color of your choice until end of turn"; +class ApostlesBlessingEffect extends OneShotEffect { + + public ApostlesBlessingEffect() { + super(Outcome.AddAbility); + this.staticText = "Target artifact or creature gains protection from artifacts or from the color of your choice until end of turn"; } - + public ApostlesBlessingEffect(final ApostlesBlessingEffect effect) { super(effect); } - + @Override public ApostlesBlessingEffect copy() { return new ApostlesBlessingEffect(this); } - + @Override public boolean apply(Game game, Ability source) { - FilterCard protectionFilter = new FilterCard(); - ChoiceColorOrArtifact choice = (ChoiceColorOrArtifact) source.getChoices().get(0); - if (choice.isArtifactSelected()) { - protectionFilter.add(new CardTypePredicate(CardType.ARTIFACT)); - } else { - protectionFilter.add(new ColorPredicate(choice.getColor())); - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + ChoiceColorOrArtifact choice = new ChoiceColorOrArtifact(); + while (!choice.isChosen()) { + if (!controller.isInGame()) { + return false; + } + controller.choose(outcome, choice, game); + } - protectionFilter.setMessage(choice.getChoice()); - ((ProtectionAbility) ability).setFilter(protectionFilter); - Permanent creature = game.getPermanent(source.getFirstTarget()); - if (creature != null) { - creature.addAbility(ability, game); + FilterCard protectionFilter = new FilterCard(); + if (choice.isArtifactSelected()) { + protectionFilter.add(new CardTypePredicate(CardType.ARTIFACT)); + } else { + protectionFilter.add(new ColorPredicate(choice.getColor())); + } + protectionFilter.setMessage(choice.getChoice()); + ProtectionAbility protectionAbility = new ProtectionAbility(protectionFilter); + ContinuousEffect effect = new GainAbilityTargetEffect(protectionAbility, Duration.EndOfTurn); + effect.setTargetPointer(getTargetPointer()); + game.addEffect(effect, source); return true; - } + } return false; } - } From 8acf28eed1b9fe82220167154c7c8aaf9d8fe51e Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 7 Jun 2015 00:53:08 +0200 Subject: [PATCH 072/102] * Added optional rollback current turn and up to 3 previous turns to the battlefield menu. All other players have to agree to the rollback to let it happen. --- .../mage/client/dialog/NewTableDialog.form | 49 +++++---- .../mage/client/dialog/NewTableDialog.java | 53 +++++---- .../client/dialog/NewTournamentDialog.form | 100 +++++++++-------- .../client/dialog/NewTournamentDialog.java | 98 +++++++++-------- .../mage/client/dialog/PreferencesDialog.java | 2 + .../mage/client/dialog/UserRequestDialog.java | 8 +- .../main/java/mage/client/game/GamePanel.java | 6 +- .../java/mage/client/game/PlayAreaPanel.java | 77 +++++++++++-- .../client/game/PlayAreaPanelOptions.java | 58 ++++++++++ Mage.Common/src/mage/view/GameView.java | 8 +- .../src/mage/game/CommanderFreeForAll.java | 4 +- .../src/mage/game/TwoPlayerDuel.java | 4 +- .../src/mage/player/human/HumanPlayer.java | 3 + .../java/mage/server/TableController.java | 6 +- .../java/mage/server/game/GameController.java | 78 ++++++++++++- .../java/mage/server/game/GameManager.java | 5 +- .../mage/server/game/GameSessionPlayer.java | 28 +++++ .../java/mage/server/game/GameWorker.java | 1 + .../sets/scarsofmirrodin/SunspearShikari.java | 17 +-- .../mage/test/serverside/PlayGameTest.java | 5 +- .../test/serverside/TestPlayRandomGame.java | 7 +- .../base/impl/CardTestPlayerAPIImpl.java | 3 +- Mage/src/mage/constants/PlayerAction.java | 4 + Mage/src/mage/game/Game.java | 9 +- Mage/src/mage/game/GameCommanderImpl.java | 4 +- Mage/src/mage/game/GameImpl.java | 104 ++++++++++++++---- Mage/src/mage/game/GameOptions.java | 5 + Mage/src/mage/game/GameState.java | 82 ++++++++------ Mage/src/mage/game/GameStates.java | 12 +- Mage/src/mage/game/GameTinyLeadersImpl.java | 4 +- Mage/src/mage/game/match/MatchOptions.java | 9 ++ Mage/src/mage/game/match/MatchPlayer.java | 10 +- Mage/src/mage/game/turn/Phase.java | 6 + Mage/src/mage/game/turn/Turn.java | 20 ++-- Mage/src/mage/players/Player.java | 1 + Mage/src/mage/players/PlayerImpl.java | 14 ++- Mage/src/mage/util/Copier.java | 4 +- Mage/src/mage/util/GameLog.java | 5 + 38 files changed, 661 insertions(+), 252 deletions(-) create mode 100644 Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form index 20d3cfa718b..01912da458f 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form @@ -32,20 +32,22 @@ - - - + + + + + - + - - - - + + + + - + @@ -89,10 +91,10 @@ - + - - + + @@ -105,7 +107,7 @@ - + @@ -133,6 +135,7 @@ + @@ -147,11 +150,13 @@ - - - + + + + + @@ -171,8 +176,8 @@ - - + + @@ -185,7 +190,7 @@ - + @@ -237,6 +242,12 @@ + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index a3f45cffdee..9f0dd4a6494 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -101,6 +101,7 @@ public class NewTableDialog extends MageDialog { cbTimeLimit = new javax.swing.JComboBox(); lblGameType = new javax.swing.JLabel(); cbGameType = new javax.swing.JComboBox(); + chkRollbackTurnsAllowed = new javax.swing.JCheckBox(); lblFreeMulligans = new javax.swing.JLabel(); spnFreeMulligans = new javax.swing.JSpinner(); lblNumPlayers = new javax.swing.JLabel(); @@ -144,6 +145,9 @@ public class NewTableDialog extends MageDialog { } }); + chkRollbackTurnsAllowed.setText("Allow rollbacks"); + chkRollbackTurnsAllowed.setToolTipText("Allow to rollback to the start of previous turns
\nif all players agree.\n"); + lblFreeMulligans.setText("Free Mulligans:"); lblFreeMulligans.setToolTipText("The number of mulligans a player can use without decreasing the number of drawn cards."); @@ -216,18 +220,20 @@ public class NewTableDialog extends MageDialog { .addComponent(lbDeckType) .addComponent(lblGameType)) .addGap(6, 6, 6) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) .addGroup(layout.createSequentialGroup() - .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, 398, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, 270, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) + .addComponent(chkRollbackTurnsAllowed) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 13, Short.MAX_VALUE) .addComponent(lblFreeMulligans) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(txtName, javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(cbDeckType, javax.swing.GroupLayout.Alignment.LEADING, 0, 338, Short.MAX_VALUE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(txtName) + .addComponent(cbDeckType, 0, 332, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lbTimeLimit, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(lblPassword, javax.swing.GroupLayout.Alignment.TRAILING)) @@ -260,10 +266,10 @@ public class NewTableDialog extends MageDialog { .addComponent(cbAttackOption, javax.swing.GroupLayout.PREFERRED_SIZE, 177, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cbSkillLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 148, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(18, 18, 18) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lblNumWins))) + .addComponent(lblNumWins) + .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE))) .addComponent(jSeparator2) .addComponent(player1Panel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) @@ -272,7 +278,7 @@ public class NewTableDialog extends MageDialog { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(jSeparator3, javax.swing.GroupLayout.DEFAULT_SIZE, 606, Short.MAX_VALUE) + .addComponent(jSeparator3, javax.swing.GroupLayout.DEFAULT_SIZE, 586, Short.MAX_VALUE) .addContainerGap())) ); layout.setVerticalGroup( @@ -294,7 +300,8 @@ public class NewTableDialog extends MageDialog { .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lblFreeMulligans)) + .addComponent(lblFreeMulligans) + .addComponent(chkRollbackTurnsAllowed)) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lblGameType))) @@ -305,11 +312,12 @@ public class NewTableDialog extends MageDialog { .addGap(0, 0, 0) .addComponent(spnNumPlayers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(lblRange) - .addComponent(lblAttack) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblSkillLevel) - .addComponent(lblNumWins)) + .addComponent(lblNumWins) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(lblRange) + .addComponent(lblAttack))) .addGap(0, 0, 0) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(cbRange, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) @@ -325,8 +333,8 @@ public class NewTableDialog extends MageDialog { .addGap(16, 16, 16) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 113, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 13, Short.MAX_VALUE) + .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 105, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 7, Short.MAX_VALUE) .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -337,7 +345,7 @@ public class NewTableDialog extends MageDialog { .addGroup(layout.createSequentialGroup() .addGap(201, 201, 201) .addComponent(jSeparator3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(178, Short.MAX_VALUE))) + .addContainerGap(167, Short.MAX_VALUE))) ); pack(); @@ -363,6 +371,7 @@ public class NewTableDialog extends MageDialog { options.setSkillLevel((SkillLevel) this.cbSkillLevel.getSelectedItem()); options.setRange((RangeOfInfluence) this.cbRange.getSelectedItem()); options.setWinsNeeded((Integer)this.spnNumWins.getValue()); + options.setRollbackTurnsAllowed(chkRollbackTurnsAllowed.isSelected()); options.setFreeMulligans((Integer)this.spnFreeMulligans.getValue()); options.setPassword(this.txtPassword.getText()); if (!checkMatchOptions(options)) { @@ -597,6 +606,9 @@ public class NewTableDialog extends MageDialog { this.player1Panel.setDeckFile(deckFile); } this.spnNumWins.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_WINS, "2"))); + this.chkRollbackTurnsAllowed.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_ROLLBACK_TURNS_ALLOWED, "Yes").equals("Yes")); + + int range = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_RANGE, "1")); for (RangeOfInfluence roi :RangeOfInfluence.values()) { if (roi.getRange() == range) { @@ -633,6 +645,8 @@ public class NewTableDialog extends MageDialog { PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_TIME_LIMIT, Integer.toString(options.getPriorityTime())); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_GAME_TYPE, options.getGameType()); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_WINS, Integer.toString(options.getWinsNeeded())); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_ROLLBACK_TURNS_ALLOWED, options.isRollbackTurnsAllowed() ? "Yes": "No"); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS, Integer.toString(options.getFreeMulligans())); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_DECK_FILE, deckFile); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_PLAYERS, spnNumPlayers.getValue().toString()); PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_RANGE, Integer.toString(options.getRange().getRange())); @@ -659,6 +673,7 @@ public class NewTableDialog extends MageDialog { private javax.swing.JComboBox cbRange; private javax.swing.JComboBox cbSkillLevel; private javax.swing.JComboBox cbTimeLimit; + private javax.swing.JCheckBox chkRollbackTurnsAllowed; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JSeparator jSeparator1; diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form index 5736b534446..9e201ee6a22 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.form @@ -51,7 +51,11 @@
- + + + + + @@ -67,52 +71,49 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + @@ -183,10 +184,11 @@ + - + @@ -440,6 +442,12 @@
+ + + + + + @@ -462,7 +470,7 @@ - + diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java index ca6a30b6f66..168442a87c8 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTournamentDialog.java @@ -164,6 +164,7 @@ public class NewTournamentDialog extends MageDialog { cbAllowSpectators = new javax.swing.JCheckBox(); lblPlayer1 = new javax.swing.JLabel(); lblConstructionTime = new javax.swing.JLabel(); + chkRollbackTurnsAllowed = new javax.swing.JCheckBox(); spnConstructTime = new javax.swing.JSpinner(); player1Panel = new mage.client.table.NewPlayerPanel(); pnlPlayers = new javax.swing.JPanel(); @@ -297,6 +298,9 @@ public class NewTournamentDialog extends MageDialog { lblConstructionTime.setText("Construction Time (Minutes):"); + chkRollbackTurnsAllowed.setText("Allow rollbacks"); + chkRollbackTurnsAllowed.setToolTipText("Allow to rollback to the start of previous turns
if all players agree. "); + spnConstructTime.setToolTipText("The time players have to build their deck."); player1Panel.setPreferredSize(new java.awt.Dimension(400, 44)); @@ -312,7 +316,7 @@ public class NewTournamentDialog extends MageDialog { ); pnlPlayersLayout.setVerticalGroup( pnlPlayersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 5, Short.MAX_VALUE) + .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 7, Short.MAX_VALUE) ); btnOk.setText("OK"); @@ -357,7 +361,10 @@ public class NewTournamentDialog extends MageDialog { .addComponent(lblConstructionTime))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(spnConstructTime, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(spnConstructTime, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(chkRollbackTurnsAllowed)) .addGroup(layout.createSequentialGroup() .addComponent(spnNumRounds, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -369,46 +376,44 @@ public class NewTournamentDialog extends MageDialog { .addComponent(btnOk) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnCancel)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblDraftCube) - .addComponent(lblTournamentType) - .addComponent(lbDeckType) - .addComponent(lblGameType)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(cbDraftCube, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(cbDeckType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGroup(layout.createSequentialGroup() - .addComponent(cbTournamentType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lblFreeMulligans) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lblNumWins) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)))) - .addGroup(layout.createSequentialGroup() - .addComponent(lblName) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(txtName, javax.swing.GroupLayout.PREFERRED_SIZE, 124, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lbTimeLimit) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cbTimeLimit, javax.swing.GroupLayout.PREFERRED_SIZE, 89, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lbSkillLevel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cbSkillLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 112, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(lblPassword) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGap(0, 0, 0)))) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblDraftCube) + .addComponent(lblTournamentType) + .addComponent(lbDeckType) + .addComponent(lblGameType)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(cbDraftCube, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(cbDeckType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(cbTournamentType, javax.swing.GroupLayout.PREFERRED_SIZE, 290, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblFreeMulligans) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblNumWins) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addGroup(layout.createSequentialGroup() + .addComponent(lblName) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(txtName, javax.swing.GroupLayout.PREFERRED_SIZE, 124, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lbTimeLimit) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cbTimeLimit, javax.swing.GroupLayout.PREFERRED_SIZE, 101, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(lbSkillLevel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(cbSkillLevel, javax.swing.GroupLayout.PREFERRED_SIZE, 88, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(lblPassword) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE))))) .addComponent(player1Panel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); @@ -464,9 +469,10 @@ public class NewTournamentDialog extends MageDialog { .addComponent(lblPlayer1, javax.swing.GroupLayout.PREFERRED_SIZE, 25, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(spnConstructTime, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(lblConstructionTime))) + .addComponent(lblConstructionTime) + .addComponent(chkRollbackTurnsAllowed))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(player1Panel, javax.swing.GroupLayout.DEFAULT_SIZE, 62, Short.MAX_VALUE) + .addComponent(player1Panel, javax.swing.GroupLayout.DEFAULT_SIZE, 64, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(pnlPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -535,6 +541,7 @@ public class NewTournamentDialog extends MageDialog { tOptions.getMatchOptions().setFreeMulligans((Integer)this.spnFreeMulligans.getValue()); tOptions.getMatchOptions().setAttackOption(MultiplayerAttackOption.LEFT); tOptions.getMatchOptions().setRange(RangeOfInfluence.ALL); + tOptions.getMatchOptions().setRollbackTurnsAllowed(this.chkRollbackTurnsAllowed.isSelected()); saveTournamentSettingsToPrefs(tOptions); table = session.createTournamentTable(roomId, tOptions); @@ -834,6 +841,7 @@ public class NewTournamentDialog extends MageDialog { } } this.cbAllowSpectators.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_ALLOW_SPECTATORS, "Yes").equals("Yes")); + this.chkRollbackTurnsAllowed.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_ALLOW_ROLLBACKS, "Yes").equals("Yes")); } private void loadBoosterPacks(String packString) { @@ -896,6 +904,7 @@ public class NewTournamentDialog extends MageDialog { } } PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_ALLOW_SPECTATORS, (tOptions.isWatchingAllowed()?"Yes":"No")); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_ALLOW_ROLLBACKS, (tOptions.getMatchOptions().isRollbackTurnsAllowed()?"Yes":"No")); } @@ -915,6 +924,7 @@ public class NewTournamentDialog extends MageDialog { private javax.swing.JComboBox cbSkillLevel; private javax.swing.JComboBox cbTimeLimit; private javax.swing.JComboBox cbTournamentType; + private javax.swing.JCheckBox chkRollbackTurnsAllowed; private javax.swing.JLabel jLabel6; private javax.swing.JLabel lbDeckType; private javax.swing.JLabel lbSkillLevel; diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 18c0d7f78c6..af889cad681 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -162,6 +162,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_NEW_TABLE_TIME_LIMIT = "newTableTimeLimit"; public static final String KEY_NEW_TABLE_GAME_TYPE = "newTableGameType"; public static final String KEY_NEW_TABLE_NUMBER_OF_WINS = "newTableNumberOfWins"; + public static final String KEY_NEW_TABLE_ROLLBACK_TURNS_ALLOWED = "newTableRollbackTurnsAllowed"; public static final String KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS = "newTableNumberOfFreeMulligans"; public static final String KEY_NEW_TABLE_DECK_FILE = "newTableDeckFile"; public static final String KEY_NEW_TABLE_RANGE = "newTableRange"; @@ -184,6 +185,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_NEW_TOURNAMENT_PLAYERS_DRAFT = "newTournamentPlayersDraft"; public static final String KEY_NEW_TOURNAMENT_DRAFT_TIMING = "newTournamentDraftTiming"; public static final String KEY_NEW_TOURNAMENT_ALLOW_SPECTATORS = "newTournamentAllowSpectators"; + public static final String KEY_NEW_TOURNAMENT_ALLOW_ROLLBACKS = "newTournamentAllowRollbacks"; public static final String KEY_NEW_TOURNAMENT_DECK_FILE = "newTournamentDeckFile"; // pref setting for deck generator diff --git a/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java b/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java index 2b859be4df2..3ecb0f35252 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/UserRequestDialog.java @@ -177,12 +177,8 @@ public class UserRequestDialog extends MageDialog { private void sendUserReplay(PlayerAction playerAction) { Session session = MageFrame.getSession(); - switch(playerAction) { - case ADD_PERMISSION_TO_SEE_HAND_CARDS: - session.sendPlayerAction(playerAction, userRequestMessage.getGameId(), userRequestMessage.getRelatedUserId()); - break; - default: - // not supported action + if (session != null && playerAction != null) { + session.sendPlayerAction(playerAction, userRequestMessage.getGameId(), userRequestMessage.getRelatedUserId()); } } diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 6c153c8a625..7f08d273407 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -483,7 +483,8 @@ public final class GamePanel extends javax.swing.JPanel { } } PlayerView player = game.getPlayers().get(playerSeat); - PlayAreaPanel sessionPlayer = new PlayAreaPanel(player, bigCard, gameId, true, game.getPriorityTime(), game.isPlayer(), this); + PlayAreaPanel sessionPlayer = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this, + new PlayAreaPanelOptions(game.isPlayer(), true, game.isRollbackTurnsAllowed())); players.put(player.getPlayerId(), sessionPlayer); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; @@ -515,7 +516,8 @@ public final class GamePanel extends javax.swing.JPanel { col = numColumns - 1; } player = game.getPlayers().get(playerNum); - PlayAreaPanel playerPanel = new PlayAreaPanel(player, bigCard, gameId, false, game.getPriorityTime(), game.isPlayer(), this); + PlayAreaPanel playerPanel = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this, + new PlayAreaPanelOptions(game.isPlayer(), false, game.isRollbackTurnsAllowed())); players.put(player.getPlayerId(), playerPanel); c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java index 1fc7bb38b9a..eef809c720e 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java @@ -42,6 +42,7 @@ import javax.swing.BorderFactory; import javax.swing.GroupLayout; import javax.swing.GroupLayout.Alignment; import javax.swing.JCheckBoxMenuItem; +import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; @@ -69,8 +70,8 @@ public class PlayAreaPanel extends javax.swing.JPanel { private boolean smallMode = false; private boolean playingMode = true; private final GamePanel gamePanel; - private final boolean playerItself; - + private final PlayAreaPanelOptions options; + private JCheckBoxMenuItem manaPoolMenuItem; private JCheckBoxMenuItem allowViewHandCardsMenuItem; @@ -81,19 +82,18 @@ public class PlayAreaPanel extends javax.swing.JPanel { * @param player * @param bigCard * @param gameId - * @param isPlayer true if the client is a player / false if the client is a watcher - * @param playerItself true if it's the area of the player itself * @param priorityTime - * @param gamePanel */ - public PlayAreaPanel(PlayerView player, BigCard bigCard, UUID gameId, boolean playerItself, int priorityTime, boolean isPlayer, GamePanel gamePanel) { - //this(isPlayer); - this.playerItself = playerItself; + * @param gamePanel + * @param options + */ + public PlayAreaPanel(PlayerView player, BigCard bigCard, UUID gameId, int priorityTime, GamePanel gamePanel, PlayAreaPanelOptions options) { + this.options = options; initComponents(); setOpaque(false); battlefieldPanel.setOpaque(false); popupMenu = new JPopupMenu(); - if (isPlayer) { + if (options.isPlayer) { addPopupMenuPlayer(player.getUserData().allowRequestShowHandCards()); } else { addPopupMenuWatcher(); @@ -242,7 +242,7 @@ public class PlayAreaPanel extends javax.swing.JPanel { popupMenu.addSeparator(); - if (!playerItself) { + if (!options.playerItself) { menuItem = new JMenuItem("Request permission to see hand cards"); popupMenu.add(menuItem); @@ -283,6 +283,63 @@ public class PlayAreaPanel extends javax.swing.JPanel { }); } popupMenu.addSeparator(); + + if (options.rollbackTurnsAllowed) { + ActionListener rollBackActionListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + int turnsToRollBack = Integer.parseInt(e.getActionCommand()); + gamePanel.getSession().sendPlayerAction(PlayerAction.ROLLBACK_TURNS, gameId, turnsToRollBack); + } + }; + + JMenu rollbackMainItem = new JMenu("Roll back"); + rollbackMainItem.setMnemonic(KeyEvent.VK_R); + rollbackMainItem.setToolTipText("The game will be rolled back to the start of the requested turn if all players agree."); + popupMenu.add(rollbackMainItem); + + menuItem = new JMenuItem("to the start of the current turn"); + menuItem.setMnemonic(KeyEvent.VK_C); + menuItem.setActionCommand("0"); + menuItem.addActionListener(rollBackActionListener); + rollbackMainItem.add(menuItem); + + menuItem = new JMenuItem("to the start of the previous turn"); + menuItem.setMnemonic(KeyEvent.VK_P); + menuItem.setActionCommand("1"); + menuItem.addActionListener(rollBackActionListener); + rollbackMainItem.add(menuItem); + + menuItem = new JMenuItem("the current turn and the 2 turns before"); + menuItem.setMnemonic(KeyEvent.VK_2); + menuItem.setActionCommand("2"); + menuItem.addActionListener(rollBackActionListener); + rollbackMainItem.add(menuItem); + + menuItem = new JMenuItem("the current turn and the 3 turns before"); + menuItem.setMnemonic(KeyEvent.VK_3); + menuItem.setActionCommand("3"); + menuItem.addActionListener(rollBackActionListener); + rollbackMainItem.add(menuItem); + + popupMenu.addSeparator(); + + } + + menuItem = new JMenuItem("Revoke all permission(s) to see your hand cards"); + menuItem.setMnemonic(KeyEvent.VK_P); + menuItem.setToolTipText("Revoke already granted permission for all spectators to see your hand cards."); + popupMenu.add(menuItem); + + // revoke permissions to see hand cards + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + gamePanel.getSession().sendPlayerAction(PlayerAction.REVOKE_PERMISSIONS_TO_SEE_HAND_CARDS, gameId, null); + } + }); + + popupMenu.addSeparator(); menuItem = new JMenuItem("Concede game"); popupMenu.add(menuItem); diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java new file mode 100644 index 00000000000..377b7591c4b --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java @@ -0,0 +1,58 @@ +/* +* 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 mage.client.game; + +/** + * Defines some options for the PlayAreaPanel + * + * @author LevelX2 + */ +public class PlayAreaPanelOptions { + + public PlayAreaPanelOptions(boolean isPlayer, boolean playerItself, boolean rollbackTurnsAllowed) { + this.isPlayer = isPlayer; + this.playerItself = playerItself; + this.rollbackTurnsAllowed = rollbackTurnsAllowed; + } + + /** + * true if the client is a player / false if the client is a watcher + */ + public boolean isPlayer = false; + + /** + * true if the player is the client player itself, false if the player is another player playing with the clinet player + */ + public boolean playerItself = false; + + /** + * true if the player can roll back turns if all players agree + */ + public boolean rollbackTurnsAllowed = false; + +} diff --git a/Mage.Common/src/mage/view/GameView.java b/Mage.Common/src/mage/view/GameView.java index 32b8be1bcd9..b75d8045131 100644 --- a/Mage.Common/src/mage/view/GameView.java +++ b/Mage.Common/src/mage/view/GameView.java @@ -81,6 +81,7 @@ public class GameView implements Serializable { private boolean special = false; private final boolean isPlayer; private final int spellsCastCurrentTurn; + private final boolean rollbackTurnsAllowed; public GameView(GameState state, Game game, UUID createdForPlayerId, UUID watcherUserId) { @@ -179,7 +180,8 @@ public class GameView implements Serializable { spellsCastCurrentTurn = watcher.getAmountOfSpellsAllPlayersCastOnCurrentTurn(); } else { spellsCastCurrentTurn = 0; - } + } + rollbackTurnsAllowed = game.getOptions().rollbackTurnsAllowed; } private void checkPaid(UUID uuid, StackAbility stackAbility) { @@ -322,5 +324,9 @@ public class GameView implements Serializable { public int getSpellsCastCurrentTurn() { return spellsCastCurrentTurn; } + + public boolean isRollbackTurnsAllowed() { + return rollbackTurnsAllowed; + } } diff --git a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java index 1dc151f90b3..81371831294 100644 --- a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java +++ b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/src/mage/game/CommanderFreeForAll.java @@ -51,9 +51,9 @@ public class CommanderFreeForAll extends GameCommanderImpl { } @Override - protected void init(UUID choosingPlayerId, GameOptions gameOptions) { + protected void init(UUID choosingPlayerId) { startingPlayerSkipsDraw = false; - super.init(choosingPlayerId, gameOptions); + super.init(choosingPlayerId); } @Override diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java index 41f2c60d263..12b68a296ea 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java @@ -59,8 +59,8 @@ public class TwoPlayerDuel extends GameImpl { } @Override - protected void init(UUID choosingPlayerId, GameOptions gameOptions) { - super.init(choosingPlayerId, gameOptions); + protected void init(UUID choosingPlayerId) { + super.init(choosingPlayerId); state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); } diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 767819d066b..537b568f6ca 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -554,6 +554,9 @@ public class HumanPlayer extends PlayerImpl { updateGameStatePriority("priority", game); game.firePriorityEvent(playerId); waitForResponse(game); + if(game.executingRollback()) { + return true; + } if (response.getBoolean() != null) { pass(game); return false; diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index d9231e36a16..4e5eceec45e 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -43,6 +43,7 @@ import mage.constants.RangeOfInfluence; import mage.constants.TableState; import mage.game.Game; import mage.game.GameException; +import mage.game.GameOptions; import mage.game.Seat; import mage.game.Table; import mage.game.draft.Draft; @@ -551,7 +552,10 @@ public class TableController { try { match.startGame(); table.initGame(); - GameManager.getInstance().createGameSession(match.getGame(), userPlayerMap, table.getId(), choosingPlayerId); + GameOptions gameOptions = new GameOptions(); + gameOptions.rollbackTurnsAllowed = match.getOptions().isRollbackTurnsAllowed(); + match.getGame().setGameOptions(gameOptions); + GameManager.getInstance().createGameSession(match.getGame(), userPlayerMap, table.getId(), choosingPlayerId, gameOptions); String creator = null; StringBuilder opponent = new StringBuilder(); for (Entry entry: userPlayerMap.entrySet()) { // no AI players diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index 39c87163cce..da30ce7fde6 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -64,6 +64,7 @@ import mage.constants.PlayerAction; import mage.constants.Zone; import mage.game.Game; import mage.game.GameException; +import mage.game.GameOptions; import mage.game.Table; import mage.game.events.Listener; import mage.game.events.PlayerQueryEvent; @@ -116,15 +117,23 @@ public class GameController implements GameCallback { private UUID choosingPlayerId; private Future gameFuture; private boolean useTimeout = true; + private GameOptions gameOptions; + + private UUID userReqestingRollback; + private int turnsToRollback; + private int requestsOpen; + - public GameController(Game game, ConcurrentHashMap userPlayerMap, UUID tableId, UUID choosingPlayerId) { + public GameController(Game game, ConcurrentHashMap userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions) { gameSessionId = UUID.randomUUID(); this.userPlayerMap = userPlayerMap; chatId = ChatManager.getInstance().createChatSession("Game " + game.getId()); + this.userReqestingRollback = null; this.game = game; this.game.setSaveGame(ConfigSettings.getInstance().isSaveGameActivated()); this.tableId = tableId; this.choosingPlayerId = choosingPlayerId; + this.gameOptions = gameOptions; for (Player player: game.getPlayers().values()) { if (!player.isHuman()) { useTimeout = false; // no timeout for AI players because of beeing idle @@ -474,6 +483,56 @@ public class GameController implements GameCallback { case UNDO: game.undo(getPlayerId(userId)); break; + case ROLLBACK_TURNS: // basic request of a player to rollback + if (data instanceof Integer) { + turnsToRollback = (Integer) data; + if (game.canRollbackTurns(turnsToRollback)) { + requestsOpen = requestPermissionToRollback(userId, turnsToRollback); + if (requestsOpen == 0) { + game.rollbackTurns(turnsToRollback); + turnsToRollback = -1; + requestsOpen = -1; + } else { + userReqestingRollback = userId; + } + } else { + UUID playerId = getPlayerId(userId); + if (playerId != null) { + Player player = game.getPlayer(playerId); + if (player != null) { + game.informPlayer(player, "That turn is not available for rollback."); + } + } + } + } + break; + case ADD_PERMISSION_TO_ROLLBACK_TURN: + if (userReqestingRollback != null && requestsOpen > 0 && !userId.equals(userReqestingRollback)) { + requestsOpen--; + if (requestsOpen == 0) { + game.rollbackTurns(turnsToRollback); + turnsToRollback = -1; + userReqestingRollback = null; + requestsOpen = -1; + } + } + break; + case DENY_PERMISSON_TO_ROLLBACK_TURN: // one player has denied - so cancel the request + { + UUID playerId = getPlayerId(userId); + if (playerId != null) { + Player player = game.getPlayer(playerId); + if (player != null) { + if (userReqestingRollback != null && requestsOpen > 0 && !userId.equals(userReqestingRollback)) { + turnsToRollback = -1; + userReqestingRollback = null; + requestsOpen = -1; + game.informPlayers("Rollback request denied by " + player.getLogName()); + } + } + } + } + break; case CONCEDE: game.concede(getPlayerId(userId)); break; @@ -513,6 +572,23 @@ public class GameController implements GameCallback { } } + private int requestPermissionToRollback(UUID userIdRequester, int numberTurns) { + int requests = 0; + for (Player player: game.getState().getPlayers().values()) { + User requestedUser = getUserByPlayerId(player.getId()); + if (player.isInGame() && player.isHuman() && + requestedUser != null && + !requestedUser.getId().equals(userIdRequester)) { + requests++; + GameSessionPlayer gameSession = gameSessions.get(player.getId()); + if (gameSession != null) { + gameSession.requestPermissionToRollbackTurn(userIdRequester, numberTurns); + } + } + } + return requests; + } + private void requestPermissionToSeeHandCards(UUID userIdRequester, UUID userIdGranter) { Player grantingPlayer = game.getPlayer(userIdGranter); if (grantingPlayer != null) { diff --git a/Mage.Server/src/main/java/mage/server/game/GameManager.java b/Mage.Server/src/main/java/mage/server/game/GameManager.java index 7075ea8e078..0b4b2e9c37e 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameManager.java +++ b/Mage.Server/src/main/java/mage/server/game/GameManager.java @@ -34,6 +34,7 @@ import mage.cards.decks.DeckCardLists; import mage.constants.ManaType; import mage.constants.PlayerAction; import mage.game.Game; +import mage.game.GameOptions; import mage.view.GameView; /** @@ -51,8 +52,8 @@ public class GameManager { private final ConcurrentHashMap gameControllers = new ConcurrentHashMap<>(); - public UUID createGameSession(Game game, ConcurrentHashMap userPlayerMap, UUID tableId, UUID choosingPlayerId) { - GameController gameController = new GameController(game, userPlayerMap, tableId, choosingPlayerId); + public UUID createGameSession(Game game, ConcurrentHashMap userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions) { + GameController gameController = new GameController(game, userPlayerMap, tableId, choosingPlayerId, gameOptions); gameControllers.put(game.getId(), gameController); return gameController.getSessionId(); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java b/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java index 2cfff99a561..0b43412b383 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java +++ b/Mage.Server/src/main/java/mage/server/game/GameSessionPlayer.java @@ -170,6 +170,34 @@ public class GameSessionPlayer extends GameSessionWatcher { } } + public void requestPermissionToRollbackTurn(UUID requestingUserId, int numberTurns) { + if (!killed) { + User requestingUser = UserManager.getInstance().getUser(requestingUserId); + User requestedUser = UserManager.getInstance().getUser(userId); + if (requestedUser != null && requestingUser != null) { + String message; + switch(numberTurns) { + case 0: + message = "Allow rollback to the start of the current turn?"; + break; + case 1: + message = "Allow rollback to the start of the previous turn?"; + break; + default: + message = "Allow to rollback "+numberTurns+ " turns?"; + } + UserRequestMessage userRequestMessage = new UserRequestMessage( + "Request by " + requestedUser.getName(), message + , PlayerAction.REQUEST_PERMISSION_TO_ROLLBACK_TURN); + userRequestMessage.setRelatedUser(requestingUserId, requestingUser.getName()); + userRequestMessage.setGameId(game.getId()); + userRequestMessage.setButton1("Accept", PlayerAction.ADD_PERMISSION_TO_ROLLBACK_TURN); + userRequestMessage.setButton2("Deny", PlayerAction.DENY_PERMISSON_TO_ROLLBACK_TURN); + requestedUser.fireCallback(new ClientCallback("userRequestDialog", game.getId(), userRequestMessage)); + } + } + } + public void requestPermissionToSeeHandCards(UUID watcherId) { if (!killed) { User watcher = UserManager.getInstance().getUser(watcherId); diff --git a/Mage.Server/src/main/java/mage/server/game/GameWorker.java b/Mage.Server/src/main/java/mage/server/game/GameWorker.java index 7a1ae9753c3..50ec2f75b95 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameWorker.java +++ b/Mage.Server/src/main/java/mage/server/game/GameWorker.java @@ -37,6 +37,7 @@ import org.apache.log4j.Logger; /** * * @author BetaSteward_at_googlemail.com + * @param */ public class GameWorker implements Callable { diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/SunspearShikari.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/SunspearShikari.java index b3f37610ae9..a52650da1c4 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/SunspearShikari.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/SunspearShikari.java @@ -32,6 +32,7 @@ import java.util.UUID; import mage.constants.CardType; import mage.constants.Rarity; import mage.MageInt; +import mage.abilities.Ability; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.EquippedCondition; import mage.abilities.decorator.ConditionalContinuousEffect; @@ -47,23 +48,23 @@ import mage.constants.Zone; */ public class SunspearShikari extends CardImpl { - private static final String rule1 = "As long as {this} is equipped, it has first strike"; - private static final String rule2 = "As long as {this} is equipped, it has lifelink"; - public SunspearShikari(UUID ownerId) { super(ownerId, 23, "Sunspear Shikari", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{W}"); this.expansionSetCode = "SOM"; this.subtype.add("Cat"); this.subtype.add("Soldier"); - this.color.setWhite(true); this.power = new MageInt(2); this.toughness = new MageInt(2); - ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance()), EquippedCondition.getInstance(), rule1); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect1)); - ConditionalContinuousEffect effect2 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(LifelinkAbility.getInstance()), EquippedCondition.getInstance(), rule2); - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect2)); + // As long as Sunspear Shikari is equipped, it has first strike and lifelink. + ConditionalContinuousEffect effect1 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance()), + EquippedCondition.getInstance(), "As long as {this} is equipped, it has first strike"); + Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, effect1); + ConditionalContinuousEffect effect2 = new ConditionalContinuousEffect(new GainAbilitySourceEffect(LifelinkAbility.getInstance()), + EquippedCondition.getInstance(), "and lifelink"); + ability.addEffect(effect2); + this.addAbility(ability); } public SunspearShikari(final SunspearShikari card) { diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java b/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java index 0e6390f9f05..230159d4d6d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/PlayGameTest.java @@ -27,7 +27,7 @@ import java.util.Random; */ public class PlayGameTest extends MageTestBase { - private static List colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu"); + private final static List colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu"); @Ignore @Test @@ -67,7 +67,8 @@ public class PlayGameTest extends MageTestBase { long t1 = System.nanoTime(); GameOptions options = new GameOptions(); options.testMode = true; - game.start(computerA.getId(), options); + game.setGameOptions(options); + game.start(computerA.getId()); long t2 = System.nanoTime(); logger.info("Winner: " + game.getWinner()); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java b/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java index 029eff40c7b..e409b738285 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/TestPlayRandomGame.java @@ -27,7 +27,7 @@ import java.util.Random; */ public class TestPlayRandomGame extends MageTestBase { - private static List colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu"); + private final static List colorChoices = Arrays.asList("bu", "bg", "br", "bw", "ug", "ur", "uw", "gr", "gw", "rw", "bur", "buw", "bug", "brg", "brw", "bgw", "wur", "wug", "wrg", "rgu"); @Test @Ignore @@ -58,12 +58,11 @@ public class TestPlayRandomGame extends MageTestBase { game.addPlayer(computerB, deck2); game.loadCards(deck2.getCards(), computerB.getId()); - boolean testMode = true; - long t1 = System.nanoTime(); GameOptions options = new GameOptions(); options.testMode = true; - game.start(computerA.getId(), options); + game.setGameOptions(options); + game.start(computerA.getId()); long t2 = System.nanoTime(); logger.info("Winner: " + game.getWinner()); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index b1eb8560ff8..c46ae3fa20a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -172,7 +172,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement gameOptions.testMode = true; gameOptions.stopOnTurn = stopOnTurn; gameOptions.stopAtStep = stopAtStep; - currentGame.start(activePlayer.getId(), gameOptions); + currentGame.setGameOptions(gameOptions); + currentGame.start(activePlayer.getId()); long t2 = System.nanoTime(); logger.debug("Winner: " + currentGame.getWinner()); logger.info("Test has been executed. Execution time: " + (t2 - t1) / 1000000 + " ms"); diff --git a/Mage/src/mage/constants/PlayerAction.java b/Mage/src/mage/constants/PlayerAction.java index ccf54950b4b..5194b113033 100644 --- a/Mage/src/mage/constants/PlayerAction.java +++ b/Mage/src/mage/constants/PlayerAction.java @@ -39,6 +39,7 @@ public enum PlayerAction { PASS_PRIORITY_UNTIL_NEXT_TURN, PASS_PRIORITY_UNTIL_STACK_RESOLVED, PASS_PRIORITY_CANCEL_ALL_ACTIONS, + ROLLBACK_TURNS, UNDO, CONCEDE, MANA_AUTO_PAYMENT_ON, @@ -46,7 +47,10 @@ public enum PlayerAction { RESET_AUTO_SELECT_REPLACEMENT_EFFECTS, REVOKE_PERMISSIONS_TO_SEE_HAND_CARDS, REQUEST_PERMISSION_TO_SEE_HAND_CARDS, + REQUEST_PERMISSION_TO_ROLLBACK_TURN, ADD_PERMISSION_TO_SEE_HAND_CARDS, + ADD_PERMISSION_TO_ROLLBACK_TURN, + DENY_PERMISSON_TO_ROLLBACK_TURN, PERMISSION_REQUESTS_ALLOWED_ON, PERMISSION_REQUESTS_ALLOWED_OFF } \ No newline at end of file diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 817c65571c4..6429c34a0ab 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -208,9 +208,7 @@ public interface Game extends MageItem, Serializable { */ PreventionEffectData preventDamage(GameEvent event, Ability source, Game game, boolean preventAllDamage); - //game play methods void start(UUID choosingPlayerId); - void start(UUID choosingPlayerId, GameOptions options); void resume(); void pause(); boolean isPaused(); @@ -293,5 +291,10 @@ public interface Game extends MageItem, Serializable { int getPriorityTime(); void setPriorityTime(int priorityTime); UUID getStartingPlayerId(); + + void saveRollBackGameState(); + boolean canRollbackTurns(int turnsToRollback); + void rollbackTurns(int turnsToRollback); + boolean executingRollback(); +} -} diff --git a/Mage/src/mage/game/GameCommanderImpl.java b/Mage/src/mage/game/GameCommanderImpl.java index 42d3ac77ef8..4a12851b2e2 100644 --- a/Mage/src/mage/game/GameCommanderImpl.java +++ b/Mage/src/mage/game/GameCommanderImpl.java @@ -75,7 +75,7 @@ public abstract class GameCommanderImpl extends GameImpl { } @Override - protected void init(UUID choosingPlayerId, GameOptions gameOptions) { + protected void init(UUID choosingPlayerId) { Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects")); //Move commander to command zone for (UUID playerId: state.getPlayerList(startingPlayerId)) { @@ -101,7 +101,7 @@ public abstract class GameCommanderImpl extends GameImpl { } this.getState().addAbility(ability, null); - super.init(choosingPlayerId, gameOptions); + super.init(choosingPlayerId); if (startingPlayerSkipsDraw) { state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); } diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index bdd68d29771..991c93727c5 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -116,6 +116,7 @@ import mage.players.Players; import mage.target.Target; import mage.target.TargetPermanent; import mage.target.TargetPlayer; +import mage.util.GameLog; import mage.util.functions.ApplyToPermanent; import mage.watchers.Watchers; import mage.watchers.common.BlockedAttackerWatcher; @@ -129,6 +130,8 @@ import org.apache.log4j.Logger; public abstract class GameImpl implements Game, Serializable { + private static final int ROLLBACK_TURNS_MAX = 4; + private static final transient Logger logger = Logger.getLogger(GameImpl.class); private static final FilterPermanent filterAura = new FilterPermanent(); @@ -155,7 +158,8 @@ public abstract class GameImpl implements Game, Serializable { private transient Object customData; protected boolean simulation = false; - protected final UUID id; + protected final UUID id; + protected boolean ready; protected transient TableEventSource tableEventSource = new TableEventSource(); protected transient PlayerQueryEventSource playerQueryEventSource = new PlayerQueryEventSource(); @@ -170,6 +174,9 @@ public abstract class GameImpl implements Game, Serializable { protected GameState state; private transient Stack savedStates = new Stack<>(); protected transient GameStates gameStates = new GameStates(); + // game states to allow player roll back + protected transient Map gameStatesRollBack = new HashMap<>(); + protected boolean executingRollback; protected Date startTime; protected Date endTime; @@ -206,7 +213,7 @@ public abstract class GameImpl implements Game, Serializable { this.attackOption = attackOption; this.state = new GameState(); this.startLife = startLife; - // this.actions = new LinkedList(); + this.executingRollback = false; } public GameImpl(final GameImpl game) { @@ -232,7 +239,6 @@ public abstract class GameImpl implements Game, Serializable { copyCount++; copyTime += (System.currentTimeMillis() - t1); } -// this.actions = new LinkedList(); this.stateCheckRequired = game.stateCheckRequired; this.scorePlayer = game.scorePlayer; this.scopeRelevant = game.scopeRelevant; @@ -268,7 +274,10 @@ public abstract class GameImpl implements Game, Serializable { @Override public GameOptions getOptions() { - return gameOptions; + if (gameOptions != null) { + return gameOptions; + } + return new GameOptions(); // happens during the first game updates } @Override @@ -610,23 +619,17 @@ public abstract class GameImpl implements Game, Serializable { return 0; } - @Override - public void start(UUID choosingPlayerId) { - start(choosingPlayerId, this.gameOptions != null ? gameOptions : GameOptions.getDefault()); - } - @Override public void cleanUp() { gameCards.clear(); } @Override - public void start(UUID choosingPlayerId, GameOptions options) { + public void start(UUID choosingPlayerId) { startTime = new Date(); - this.gameOptions = options; if (state.getPlayers().values().iterator().hasNext()) { scorePlayer = state.getPlayers().values().iterator().next(); - init(choosingPlayerId, options); + init(choosingPlayerId); play(startingPlayerId); } } @@ -731,13 +734,26 @@ public abstract class GameImpl implements Game, Serializable { } private boolean playTurn(Player player) { - this.logStartOfTurn(player); - if (checkStopOnTurnOption()) { - return false; - } - state.setActivePlayerId(player.getId()); - player.becomesActivePlayer(); - state.getTurn().play(this, player.getId()); + do { + if (executingRollback) { + executingRollback = false; + player = getPlayer(state.getActivePlayerId()); + for (Player playerObject: getPlayers().values()) { + if (playerObject.isInGame()) { + playerObject.abortReset(); + } + } + } else { + state.setActivePlayerId(player.getId()); + saveRollBackGameState(); + } + this.logStartOfTurn(player); + if (checkStopOnTurnOption()) { + return false; + } + state.getTurn().play(this, player); + } while (executingRollback); + if (isPaused() || gameOver(null)) { return false; } @@ -778,7 +794,7 @@ public abstract class GameImpl implements Game, Serializable { return false; } - protected void init(UUID choosingPlayerId, GameOptions gameOptions) { + protected void init(UUID choosingPlayerId) { for (Player player: state.getPlayers().values()) { player.beginTurn(this); // init only if match is with timer (>0) and time left was not set yet (== MAX_VALUE). @@ -1151,6 +1167,9 @@ public abstract class GameImpl implements Game, Serializable { } // resetPassed should be called if player performs any action if (player.priority(this)) { + if(executingRollback()) { + return; + } applyEffects(); } if (isPaused()) { @@ -2566,6 +2585,51 @@ public abstract class GameImpl implements Game, Serializable { } } + @Override + public void saveRollBackGameState() { + if (gameOptions.rollbackTurnsAllowed) { + int toDelete = getTurnNum()- ROLLBACK_TURNS_MAX; + if (toDelete > 0 && gameStatesRollBack.containsKey(toDelete)) { + gameStatesRollBack.remove(toDelete); + } + gameStatesRollBack.put(getTurnNum(), state.copy()); + } + } + @Override + public boolean canRollbackTurns(int turnsToRollback) { + int turnToGoTo = getTurnNum() - turnsToRollback; + return turnToGoTo > 0 && gameStatesRollBack.containsKey(turnToGoTo); + } + + @Override + public synchronized void rollbackTurns(int turnsToRollback) { + if (gameOptions.rollbackTurnsAllowed) { + int turnToGoTo = getTurnNum() - turnsToRollback; + if (turnToGoTo < 1 || !gameStatesRollBack.containsKey(turnToGoTo)) { + informPlayers(GameLog.getPlayerRequestColoredText("Player request: It's not possible to rollback " + turnsToRollback +" turn(s)")); + } else { + GameState restore = gameStatesRollBack.get(turnToGoTo); + if (restore != null) { + informPlayers(GameLog.getPlayerRequestColoredText("Player request: Rolling back to start of turn " + restore.getTurnNum())); + for (Player playerObject: getPlayers().values()) { + if (playerObject.isHuman() && playerObject.isInGame()) { + playerObject.abort(); + } + } + state.restore(restore); + // because restore uses the objects without copy each copy the state again + gameStatesRollBack.put(getTurnNum(), state.copy()); + executingRollback = true; + fireUpdatePlayersEvent(); + } + } + } + } + + @Override + public boolean executingRollback() { + return executingRollback; + } } diff --git a/Mage/src/mage/game/GameOptions.java b/Mage/src/mage/game/GameOptions.java index 63ba1888368..7767880a589 100644 --- a/Mage/src/mage/game/GameOptions.java +++ b/Mage/src/mage/game/GameOptions.java @@ -37,4 +37,9 @@ public class GameOptions implements Serializable { * If true, library won't be shuffled at the beginning of the game */ public boolean skipInitShuffling = false; + + /** + * If true, players can roll back turn if all players agree + */ + public boolean rollbackTurnsAllowed = true; } diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index 8e82a48f4ff..a29a353ee67 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -77,19 +77,19 @@ public class GameState implements Serializable, Copyable { private final Players players; private final PlayerList playerList; - private final Turn turn; + private UUID choosingPlayerId; // player that makes a choice at game start + // revealed cards >, will be reset if all players pass priority private final Revealed revealed; private final Map lookedAt = new HashMap<>(); - private final DelayedTriggeredAbilities delayed; - private final SpecialActions specialActions; - private final TurnMods turnMods; - private final Watchers watchers; - + private DelayedTriggeredAbilities delayed; + private SpecialActions specialActions; + private Watchers watchers; + private Turn turn; + private TurnMods turnMods; private UUID activePlayerId; // playerId which turn it is private UUID priorityPlayerId; // player that has currently priority - private UUID choosingPlayerId; // player that makes a choice at game start private SpellStack stack; private Command command; private Exile exile; @@ -134,21 +134,24 @@ public class GameState implements Serializable, Copyable { public GameState(final GameState state) { this.players = state.players.copy(); this.playerList = state.playerList.copy(); + this.choosingPlayerId = state.choosingPlayerId; + this.revealed = state.revealed.copy(); + this.lookedAt.putAll(state.lookedAt); + this.gameOver = state.gameOver; + this.paused = state.paused; + this.activePlayerId = state.activePlayerId; this.priorityPlayerId = state.priorityPlayerId; - this.choosingPlayerId = state.choosingPlayerId; this.turn = state.turn.copy(); + this.stack = state.stack.copy(); this.command = state.command.copy(); this.exile = state.exile.copy(); - this.revealed = state.revealed.copy(); - this.lookedAt.putAll(state.lookedAt); this.battlefield = state.battlefield.copy(); this.turnNum = state.turnNum; this.stepNum = state.stepNum; this.extraTurn = state.extraTurn; this.legendaryRuleActive = state.legendaryRuleActive; - this.gameOver = state.gameOver; this.effects = state.effects.copy(); for (TriggeredAbility trigger: state.triggered) { this.triggered.add(trigger.copy()); @@ -163,7 +166,6 @@ public class GameState implements Serializable, Copyable { this.values.put(entry.getKey(), entry.getValue()); } this.zones.putAll(state.zones); - this.paused = state.paused; this.simultaneousEvents.addAll(state.simultaneousEvents); for (Map.Entry entry: state.cardState.entrySet()) { cardState.put(entry.getKey(), entry.getValue().copy()); @@ -172,6 +174,40 @@ public class GameState implements Serializable, Copyable { this.copiedCards.putAll(state.copiedCards); this.permanentOrderNumber = state.permanentOrderNumber; } + + public void restore(GameState state) { + this.activePlayerId = state.activePlayerId; + this.priorityPlayerId = state.priorityPlayerId; + this.turn = state.turn; + + this.stack = state.stack; + this.command = state.command; + this.exile = state.exile; + this.battlefield = state.battlefield; + this.turnNum = state.turnNum; + this.stepNum = state.stepNum; + this.extraTurn = state.extraTurn; + this.legendaryRuleActive = state.legendaryRuleActive; + this.effects = state.effects; + this.triggered = state.triggered; + this.triggers = state.triggers; + this.delayed = state.delayed; + this.specialActions = state.specialActions; + this.combat = state.combat; + this.turnMods = state.turnMods; + this.watchers = state.watchers; + this.values = state.values; + for (Player copyPlayer: state.players.values()) { + Player origPlayer = players.get(copyPlayer.getId()); + origPlayer.restore(copyPlayer); + } + this.zones = state.zones; + this.simultaneousEvents = state.simultaneousEvents; + this.cardState = state.cardState; + this.zoneChangeCounter = state.zoneChangeCounter; + this.copiedCards = state.copiedCards; + this.permanentOrderNumber = state.permanentOrderNumber; + } @Override public GameState copy() { @@ -558,28 +594,6 @@ public class GameState implements Serializable, Copyable { zones.put(id, zone); } - public void restore(GameState state) { - this.stack = state.stack; - this.command = state.command; - this.effects = state.effects; - this.triggers = state.triggers; - this.triggered = state.triggered; - this.combat = state.combat; - this.exile = state.exile; - this.battlefield = state.battlefield; - this.zones = state.zones; - this.values = state.values; - for (Player copyPlayer: state.players.values()) { - Player origPlayer = players.get(copyPlayer.getId()); - origPlayer.restore(copyPlayer); - } - this.simultaneousEvents = state.simultaneousEvents; - this.cardState = state.cardState; - this.zoneChangeCounter = state.zoneChangeCounter; - this.copiedCards = state.copiedCards; - this.permanentOrderNumber = state.permanentOrderNumber; - } - public void addSimultaneousEvent(GameEvent event, Game game) { simultaneousEvents.add(event); } diff --git a/Mage/src/mage/game/GameStates.java b/Mage/src/mage/game/GameStates.java index ab624bb9978..e88e45c27d7 100644 --- a/Mage/src/mage/game/GameStates.java +++ b/Mage/src/mage/game/GameStates.java @@ -42,8 +42,12 @@ public class GameStates implements Serializable { private static final transient Logger logger = Logger.getLogger(GameStates.class); -// private List states = new LinkedList(); - private final List states = new LinkedList<>(); +// private final List states; + private final List states; + + public GameStates() { + this.states = new LinkedList<>(); + } public void save(GameState gameState) { // states.add(new Copier().copyCompressed(gameState)); @@ -60,8 +64,8 @@ public class GameStates implements Serializable { while (states.size() > index + 1) { states.remove(states.size() - 1); } -// return new Copier().uncompressCopy(states.get(index)); logger.trace("Rolling back state: " + index); +// return new Copier().uncompressCopy(states.get(index)); return states.get(index); } return null; @@ -78,7 +82,7 @@ public class GameStates implements Serializable { public GameState get(int index) { if (index < states.size()) { - // return new Copier().uncompressCopy(states.get(index)); +// return new Copier().uncompressCopy(states.get(index)); return states.get(index); } return null; diff --git a/Mage/src/mage/game/GameTinyLeadersImpl.java b/Mage/src/mage/game/GameTinyLeadersImpl.java index d25f23cea01..6addf9f0c9e 100644 --- a/Mage/src/mage/game/GameTinyLeadersImpl.java +++ b/Mage/src/mage/game/GameTinyLeadersImpl.java @@ -74,7 +74,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl{ } @Override - protected void init(UUID choosingPlayerId, GameOptions gameOptions) { + protected void init(UUID choosingPlayerId) { Ability ability = new SimpleStaticAbility(Zone.COMMAND, new InfoEffect("Commander effects")); //Move tiny leader to command zone for (UUID playerId: state.getPlayerList(startingPlayerId)) { @@ -101,7 +101,7 @@ public abstract class GameTinyLeadersImpl extends GameImpl{ } this.getState().addAbility(ability, null); - super.init(choosingPlayerId, gameOptions); + super.init(choosingPlayerId); if (startingPlayerSkipsDraw) { state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); } diff --git a/Mage/src/mage/game/match/MatchOptions.java b/Mage/src/mage/game/match/MatchOptions.java index 71ff1cd2501..7fce8625475 100644 --- a/Mage/src/mage/game/match/MatchOptions.java +++ b/Mage/src/mage/game/match/MatchOptions.java @@ -53,6 +53,7 @@ public class MatchOptions implements Serializable { protected List playerTypes = new ArrayList<>(); protected String password; protected SkillLevel skillLevel; + protected boolean rollbackTurnsAllowed; /** * Time each player has during the game to play using his\her priority. @@ -159,4 +160,12 @@ public class MatchOptions implements Serializable { public void setSkillLevel(SkillLevel skillLevel) { this.skillLevel = skillLevel; } + + public boolean isRollbackTurnsAllowed() { + return rollbackTurnsAllowed; + } + + public void setRollbackTurnsAllowed(boolean rollbackTurnsAllowed) { + this.rollbackTurnsAllowed = rollbackTurnsAllowed; + } } diff --git a/Mage/src/mage/game/match/MatchPlayer.java b/Mage/src/mage/game/match/MatchPlayer.java index af956dcc747..6f77ebfe8a5 100644 --- a/Mage/src/mage/game/match/MatchPlayer.java +++ b/Mage/src/mage/game/match/MatchPlayer.java @@ -28,6 +28,7 @@ package mage.game.match; +import java.io.Serializable; import mage.cards.Card; import mage.cards.decks.Deck; import mage.players.Player; @@ -36,7 +37,10 @@ import mage.players.Player; * * @author BetaSteward_at_googlemail.com */ -public class MatchPlayer { +public class MatchPlayer implements Serializable { + + private static final long serialVersionUID = 42L; + private int wins; private boolean matchWinner; @@ -45,7 +49,7 @@ public class MatchPlayer { private final String name; private boolean quit; - private final boolean timerTimeout; + //private final boolean timerTimeout; private boolean doneSideboarding; private int priorityTimeLeft; @@ -56,7 +60,7 @@ public class MatchPlayer { this.wins = 0; this.doneSideboarding = true; this.quit = false; - this.timerTimeout = false; + //this.timerTimeout = false; this.name = player.getName(); this.matchWinner = false; } diff --git a/Mage/src/mage/game/turn/Phase.java b/Mage/src/mage/game/turn/Phase.java index 8881f21c1c1..0fc741fcc4f 100644 --- a/Mage/src/mage/game/turn/Phase.java +++ b/Mage/src/mage/game/turn/Phase.java @@ -113,6 +113,9 @@ public abstract class Phase implements Serializable { currentStep = step; if (!game.getState().getTurnMods().skipStep(activePlayerId, getStep().getType())) { playStep(game); + if (game.executingRollback()) { + return true; + } } if (!game.isSimulation() && checkStopOnStepOption(game)) { return false; @@ -201,6 +204,9 @@ public abstract class Phase implements Serializable { prePriority(game, activePlayerId); if (!game.isPaused() && !game.gameOver(null)) { currentStep.priority(game, activePlayerId, false); + if(game.executingRollback()) { + return; + } } if (!game.isPaused() && !game.gameOver(null)) { postPriority(game, activePlayerId); diff --git a/Mage/src/mage/game/turn/Turn.java b/Mage/src/mage/game/turn/Turn.java index da1b3b830f8..c781a711d2d 100644 --- a/Mage/src/mage/game/turn/Turn.java +++ b/Mage/src/mage/game/turn/Turn.java @@ -117,30 +117,34 @@ public class Turn implements Serializable { return null; } - public void play(Game game, UUID activePlayerId) { + public void play(Game game, Player activePlayer) { + activePlayer.becomesActivePlayer(); this.setDeclareAttackersStepStarted(false); if (game.isPaused() || game.gameOver(null)) { return; } - if (game.getState().getTurnMods().skipTurn(activePlayerId)) { + if (game.getState().getTurnMods().skipTurn(activePlayer.getId())) { return; } - checkTurnIsControlledByOtherPlayer(game, activePlayerId); + checkTurnIsControlledByOtherPlayer(game, activePlayer.getId()); - this.activePlayerId = activePlayerId; + this.activePlayerId = activePlayer.getId(); resetCounts(); - game.getPlayer(activePlayerId).beginTurn(game); + game.getPlayer(activePlayer.getId()).beginTurn(game); for (Phase phase: phases) { if (game.isPaused() || game.gameOver(null)) { return; } if (!isEndTurnRequested() || phase.getType().equals(TurnPhase.END)) { currentPhase = phase; - game.fireEvent(new GameEvent(GameEvent.EventType.PHASE_CHANGED, activePlayerId, null, activePlayerId)); - if (!game.getState().getTurnMods().skipPhase(activePlayerId, currentPhase.getType())) { - if (phase.play(game, activePlayerId)) { + game.fireEvent(new GameEvent(GameEvent.EventType.PHASE_CHANGED, activePlayer.getId(), null, activePlayer.getId())); + if (!game.getState().getTurnMods().skipPhase(activePlayer.getId(), currentPhase.getType())) { + if (phase.play(game, activePlayer.getId())) { + if(game.executingRollback()) { + return; + } //20091005 - 500.4/703.4n game.emptyManaPools(); game.saveState(false); diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index e6c4338ab32..603744d0655 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -280,6 +280,7 @@ public interface Player extends MageItem, Copyable { void leave(); void concede(Game game); void abort(); + void abortReset(); void skip(); // priority, undo, ... diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index cc5236331e0..da437f09435 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -369,8 +369,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.canPaySacrificeCost = player.canPaySacrificeCost(); this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife(); this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard(); - this.alternativeSourceCosts.addAll(player.getAlternativeSourceCosts()); - this.storedBookmark = player.getStoredBookmark(); + this.alternativeSourceCosts.addAll(player.getAlternativeSourceCosts()); this.topCardRevealed = player.isTopCardRevealed(); this.playersUnderYourControl.clear(); @@ -385,7 +384,9 @@ public abstract class PlayerImpl implements Player, Serializable { this.castSourceIdWithAlternateMana = player.getCastSourceIdWithAlternateMana(); this.castSourceIdManaCosts = player.getCastSourceIdManaCosts(); - this.usersAllowedToSeeHandCards.addAll(player.getUsersAllowedToSeeHandCards()); + // Don't restore! + // this.storedBookmark + // this.usersAllowedToSeeHandCards } @Override @@ -1855,7 +1856,7 @@ public abstract class PlayerImpl implements Player, Serializable { passedAllTurns = false; passedUntilEndOfTurn = true; passedUntilStackResolved = false; - skippedAtLeastOnce = !game.getTurn().getStepType().equals(PhaseStep.END_TURN); + skippedAtLeastOnce = !PhaseStep.END_TURN.equals(game.getTurn().getStepType()); this.skip(); break; case PASS_PRIORITY_UNTIL_NEXT_TURN: // F4 @@ -3126,4 +3127,9 @@ public abstract class PlayerImpl implements Player, Serializable { return matchPlayer; } + @Override + public void abortReset() { + abort = false; + } + } diff --git a/Mage/src/mage/util/Copier.java b/Mage/src/mage/util/Copier.java index 613723ce17d..4830d61bf2c 100644 --- a/Mage/src/mage/util/Copier.java +++ b/Mage/src/mage/util/Copier.java @@ -38,6 +38,7 @@ import java.util.zip.GZIPOutputStream; /** * * @author BetaSteward_at_googlemail.com + * @param */ public class Copier { @@ -95,8 +96,7 @@ public class Copier { public T uncompressCopy(byte[] buffer) { T copy = null; - try { - ObjectInputStream in = new CopierObjectInputStream(loader, new GZIPInputStream(new ByteArrayInputStream(buffer))); + try (ObjectInputStream in = new CopierObjectInputStream(loader, new GZIPInputStream(new ByteArrayInputStream(buffer)))) { copy = (T) in.readObject(); } catch(IOException e) { diff --git a/Mage/src/mage/util/GameLog.java b/Mage/src/mage/util/GameLog.java index 27887668cd3..c71081c96dc 100644 --- a/Mage/src/mage/util/GameLog.java +++ b/Mage/src/mage/util/GameLog.java @@ -38,6 +38,7 @@ import mage.ObjectColor; public class GameLog { static final String LOG_COLOR_PLAYER = "#20B2AA"; // LightSeaGreen + static final String LOG_COLOR_PLAYER_REQUEST = "#D2691E"; // Chocolate static final String LOG_COLOR_GREEN = "#90EE90"; // LightGreen static final String LOG_COLOR_RED = "#FF6347"; // Tomato static final String LOG_COLOR_BLUE = "#87CEFA"; // LightSkyBlue @@ -64,6 +65,10 @@ public class GameLog { public static String getColoredPlayerName(String name) { return "" + name + ""; } + + public static String getPlayerRequestColoredText(String name) { + return "" + name + ""; + } private static String getColorName(ObjectColor objectColor) { if (objectColor.isMulticolored()) { From f2cf08d5b20ffa7b8194d0aa8d865fcb3b459dde Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 7 Jun 2015 01:07:49 +0200 Subject: [PATCH 073/102] Cleaned up ReturnToHandChosenControllesPermanentEffect. --- .../mage/sets/planarchaos/DustElemental.java | 9 +---- .../sets/planarchaos/StormfrontRiders.java | 2 +- ...ToHandChosenControlledPermanentEffect.java | 37 ++++++------------- 3 files changed, 14 insertions(+), 34 deletions(-) diff --git a/Mage.Sets/src/mage/sets/planarchaos/DustElemental.java b/Mage.Sets/src/mage/sets/planarchaos/DustElemental.java index 34035273a96..2e910edd37a 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/DustElemental.java +++ b/Mage.Sets/src/mage/sets/planarchaos/DustElemental.java @@ -29,7 +29,6 @@ package mage.sets.planarchaos; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.effects.common.ReturnToHandChosenControlledPermanentEffect; import mage.abilities.keyword.FlashAbility; @@ -38,12 +37,7 @@ import mage.abilities.keyword.FearAbility; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; -import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetControlledPermanent; /** * @@ -65,8 +59,9 @@ public class DustElemental extends CardImpl { // Flying; fear this.addAbility(FlyingAbility.getInstance()); this.addAbility(FearAbility.getInstance()); + // When Dust Elemental enters the battlefield, return three creatures you control to their owner's hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(filter,1,3))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(filter, 3))); } public DustElemental(final DustElemental card) { diff --git a/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java b/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java index e2415496f27..e9804d375cb 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java +++ b/Mage.Sets/src/mage/sets/planarchaos/StormfrontRiders.java @@ -68,7 +68,7 @@ public class StormfrontRiders extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Stormfront Riders enters the battlefield, return two creatures you control to their owner's hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(new FilterControlledCreaturePermanent("creatures you control"),1,2))); + this.addAbility(new EntersBattlefieldTriggeredAbility(new ReturnToHandChosenControlledPermanentEffect(new FilterControlledCreaturePermanent("creatures you control"), 2))); // Whenever Stormfront Riders or another creature is returned to your hand from the battlefield, put a 1/1 white Soldier creature token onto the battlefield. this.addAbility(new ZoneChangeAllTriggeredAbility(Zone.BATTLEFIELD, Zone.BATTLEFIELD, Zone.HAND, new CreateTokenEffect(new SoldierToken()), filter,"Whenever {this} or another creature is returned to your hand from the battlefield, ", false)); diff --git a/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java b/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java index a5aabf6310f..e5cf0b29436 100644 --- a/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java +++ b/Mage/src/mage/abilities/effects/common/ReturnToHandChosenControlledPermanentEffect.java @@ -27,14 +27,13 @@ */ package mage.abilities.effects.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; +import mage.cards.CardsImpl; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterControlledPermanent; import mage.game.Game; -import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetControlledPermanent; import mage.util.CardUtil; @@ -44,27 +43,18 @@ import mage.util.CardUtil; * @author Plopmans */ public class ReturnToHandChosenControlledPermanentEffect extends OneShotEffect { - + private final FilterControlledPermanent filter; private int number; - private int minNumber; - + public ReturnToHandChosenControlledPermanentEffect(FilterControlledPermanent filter) { this(filter, 1); } + public ReturnToHandChosenControlledPermanentEffect(FilterControlledPermanent filter, int number) { super(Outcome.ReturnToHand); this.filter = filter; this.number = number; - this.minNumber = number; - this.staticText = getText(); - } - - public ReturnToHandChosenControlledPermanentEffect(FilterControlledPermanent filter, int minNumber, int maxNumber) { - super(Outcome.ReturnToHand); - this.filter = filter; - this.number = maxNumber; - this.minNumber = minNumber; this.staticText = getText(); } @@ -72,10 +62,7 @@ public class ReturnToHandChosenControlledPermanentEffect extends OneShotEffect { super(effect); this.filter = effect.filter; this.number = effect.number; - this.minNumber = this.minNumber; } - - @Override public ReturnToHandChosenControlledPermanentEffect copy() { @@ -84,15 +71,13 @@ public class ReturnToHandChosenControlledPermanentEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - TargetControlledPermanent target = new TargetControlledPermanent(minNumber, number, filter, true); - if (player.choose(this.outcome, target, source.getSourceId(), game)) { - for (UUID targetCreatureId : target.getTargets()) { - Permanent permanent = game.getPermanent(targetCreatureId); - if (permanent != null) { - player.moveCardToHandWithInfo(permanent, source.getSourceId(), game, Zone.BATTLEFIELD); - } + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + int available = game.getBattlefield().count(filter, source.getSourceId(), source.getControllerId(), game); + if (available > 0) { + TargetControlledPermanent target = new TargetControlledPermanent(Math.min(number, available), number, filter, true); + if (controller.chooseTarget(this.outcome, target, source, game)) { + controller.moveCards(new CardsImpl(target.getTargets()), Zone.BATTLEFIELD, Zone.HAND, source, game); } } return true; From 8066fe911c2c9466988567f056d9bacca42fc2fa Mon Sep 17 00:00:00 2001 From: emerald000 Date: Sat, 6 Jun 2015 19:32:47 -0400 Subject: [PATCH 074/102] Added Forcefield. --- .../mage/sets/limitedalpha/Forcefield.java | 52 ++++++ .../src/mage/sets/limitedbeta/Forcefield.java | 52 ++++++ .../mage/sets/mastersedition/Forcefield.java | 151 ++++++++++++++++++ .../sets/unlimitededition/Forcefield.java | 52 ++++++ .../permanent/UnblockedPredicate.java | 3 +- 5 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 Mage.Sets/src/mage/sets/limitedalpha/Forcefield.java create mode 100644 Mage.Sets/src/mage/sets/limitedbeta/Forcefield.java create mode 100644 Mage.Sets/src/mage/sets/mastersedition/Forcefield.java create mode 100644 Mage.Sets/src/mage/sets/unlimitededition/Forcefield.java diff --git a/Mage.Sets/src/mage/sets/limitedalpha/Forcefield.java b/Mage.Sets/src/mage/sets/limitedalpha/Forcefield.java new file mode 100644 index 00000000000..9e04886c363 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedalpha/Forcefield.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.limitedalpha; + +import java.util.UUID; + +/** + * + * @author emerald000 + */ +public class Forcefield extends mage.sets.mastersedition.Forcefield { + + public Forcefield(UUID ownerId) { + super(ownerId); + this.cardNumber = 243; + this.expansionSetCode = "LEA"; + } + + public Forcefield(final Forcefield card) { + super(card); + } + + @Override + public Forcefield copy() { + return new Forcefield(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedbeta/Forcefield.java b/Mage.Sets/src/mage/sets/limitedbeta/Forcefield.java new file mode 100644 index 00000000000..5ed2b347522 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedbeta/Forcefield.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.limitedbeta; + +import java.util.UUID; + +/** + * + * @author emerald000 + */ +public class Forcefield extends mage.sets.mastersedition.Forcefield { + + public Forcefield(UUID ownerId) { + super(ownerId); + this.cardNumber = 245; + this.expansionSetCode = "LEB"; + } + + public Forcefield(final Forcefield card) { + super(card); + } + + @Override + public Forcefield copy() { + return new Forcefield(this); + } +} diff --git a/Mage.Sets/src/mage/sets/mastersedition/Forcefield.java b/Mage.Sets/src/mage/sets/mastersedition/Forcefield.java new file mode 100644 index 00000000000..cc7ebcd2527 --- /dev/null +++ b/Mage.Sets/src/mage/sets/mastersedition/Forcefield.java @@ -0,0 +1,151 @@ +/* + * 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 mage.sets.mastersedition; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.ContinuousEffect; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.PreventionEffectImpl; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.permanent.UnblockedPredicate; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.players.Player; +import mage.target.Target; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author emerald000 + */ +public class Forcefield extends CardImpl { + + public Forcefield(UUID ownerId) { + super(ownerId, 157, "Forcefield", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "MED"; + + // {1}: The next time an unblocked creature of your choice would deal combat damage to you this turn, prevent all but 1 of that damage. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ForcefieldEffect(), new GenericManaCost(1))); + } + + public Forcefield(final Forcefield card) { + super(card); + } + + @Override + public Forcefield copy() { + return new Forcefield(this); + } +} + +class ForcefieldEffect extends OneShotEffect { + + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("an unblocked creature"); + static { + filter.add(new UnblockedPredicate()); + } + + ForcefieldEffect() { + super(Outcome.PreventDamage); + this.staticText = "The next time an unblocked creature of your choice would deal combat damage to you this turn, prevent all but 1 of that damage"; + } + + ForcefieldEffect(final ForcefieldEffect effect) { + super(effect); + } + + @Override + public ForcefieldEffect copy() { + return new ForcefieldEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Target target = new TargetCreaturePermanent(1, 1, filter, true); + if (controller.choose(Outcome.PreventDamage, target, source.getSourceId(), game)) { + ContinuousEffect effect = new ForcefieldPreventionEffect(); + effect.setTargetPointer(new FixedTarget(target.getFirstTarget())); + game.addEffect(effect, source); + } + return true; + } + return false; + } +} + +class ForcefieldPreventionEffect extends PreventionEffectImpl { + + ForcefieldPreventionEffect() { + super(Duration.EndOfTurn, Integer.MAX_VALUE, true, false); + this.staticText = "Prevent all but 1 of that damage"; + } + + ForcefieldPreventionEffect(ForcefieldPreventionEffect effect) { + super(effect); + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + int damage = event.getAmount(); + if (damage > 0) { + this.amountToPrevent = damage - 1; + preventDamageAction(event, source, game); + this.discard(); + } + return false; + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType() == EventType.DAMAGE_PLAYER; + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + return super.applies(event, source, game) + && event.getSourceId().equals(this.getTargetPointer().getFirst(game, source)); + } + + @Override + public ForcefieldPreventionEffect copy() { + return new ForcefieldPreventionEffect(this); + } +} diff --git a/Mage.Sets/src/mage/sets/unlimitededition/Forcefield.java b/Mage.Sets/src/mage/sets/unlimitededition/Forcefield.java new file mode 100644 index 00000000000..f91d69dfe09 --- /dev/null +++ b/Mage.Sets/src/mage/sets/unlimitededition/Forcefield.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.unlimitededition; + +import java.util.UUID; + +/** + * + * @author emerald000 + */ +public class Forcefield extends mage.sets.mastersedition.Forcefield { + + public Forcefield(UUID ownerId) { + super(ownerId); + this.cardNumber = 244; + this.expansionSetCode = "2ED"; + } + + public Forcefield(final Forcefield card) { + super(card); + } + + @Override + public Forcefield copy() { + return new Forcefield(this); + } +} diff --git a/Mage/src/mage/filter/predicate/permanent/UnblockedPredicate.java b/Mage/src/mage/filter/predicate/permanent/UnblockedPredicate.java index 46cbb07245d..b5667793083 100644 --- a/Mage/src/mage/filter/predicate/permanent/UnblockedPredicate.java +++ b/Mage/src/mage/filter/predicate/permanent/UnblockedPredicate.java @@ -48,7 +48,8 @@ public class UnblockedPredicate implements Predicate { if ((game.getPhase().getStep().getType() == PhaseStep.DECLARE_BLOCKERS && game.getStep().getStepPart() == Step.StepPart.PRIORITY) || game.getPhase().getStep().getType() == PhaseStep.FIRST_COMBAT_DAMAGE - || game.getPhase().getStep().getType() == PhaseStep.COMBAT_DAMAGE) { + || game.getPhase().getStep().getType() == PhaseStep.COMBAT_DAMAGE + || game.getPhase().getStep().getType() == PhaseStep.END_COMBAT) { CombatGroup combatGroup = game.getCombat().findGroup(input.getId()); if (combatGroup != null) { return combatGroup.getBlockers().isEmpty(); From 907ec7abb03cdf3835babd22ea8908ff8dd6fa94 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 7 Jun 2015 17:14:43 +0200 Subject: [PATCH 075/102] Added new option for mana payment handling of mana already in the mana pool. Reworked the battlefield context menu a bit. --- .../mage/client/dialog/PreferencesDialog.java | 1 + .../main/java/mage/client/game/GamePanel.java | 10 +- .../java/mage/client/game/PlayAreaPanel.java | 296 ++++++++++-------- .../src/mage/remote/interfaces/GamePlay.java | 1 + .../java/mage/server/game/GameController.java | 10 +- Mage/src/mage/constants/PlayerAction.java | 2 + Mage/src/mage/game/Game.java | 3 +- Mage/src/mage/game/GameImpl.java | 10 +- Mage/src/mage/players/ManaPool.java | 46 +++ Mage/src/mage/players/ManaPoolItem.java | 22 +- Mage/src/mage/players/PlayerImpl.java | 1 + 11 files changed, 263 insertions(+), 139 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index af889cad681..5221a550519 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -134,6 +134,7 @@ public class PreferencesDialog extends javax.swing.JDialog { // mana auto payment public static final String KEY_GAME_MANA_AUTOPAYMENT = "gameManaAutopayment"; + public static final String KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE = "gameManaAutopaymentOnlyOne"; // Size of frame to check if divider locations should be used public static final String KEY_MAGE_PANEL_LAST_SIZE = "gamepanelLastSize"; diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 7f08d273407..029e928fd74 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -91,6 +91,7 @@ import mage.client.dialog.PickNumberDialog; import mage.client.dialog.PickPileDialog; import mage.client.dialog.PreferencesDialog; import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT; +import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE; import mage.client.dialog.ShowCardsDialog; import mage.client.game.FeedbackPanel.FeedbackMode; import mage.client.plugins.adapters.MageActionCallback; @@ -459,7 +460,9 @@ public final class GamePanel extends javax.swing.JPanel { public synchronized void init(GameView game) { addPlayers(game); // default menu states - setMenuStates(PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT, "true").equals("true")); + setMenuStates( + PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT, "true").equals("true"), + PreferencesDialog.getCachedValue(KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE, "true").equals("true")); updateGame(game); } @@ -734,10 +737,11 @@ public final class GamePanel extends javax.swing.JPanel { /** * Set the same state for menu selections to all player areas. * @param manaPoolAutomatic + * @param manaPoolAutomaticRestricted */ - public void setMenuStates(boolean manaPoolAutomatic) { + public void setMenuStates(boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted) { for(PlayAreaPanel playAreaPanel: players.values()) { - playAreaPanel.setMenuStates(manaPoolAutomatic); + playAreaPanel.setMenuStates(manaPoolAutomatic, manaPoolAutomaticRestricted); } } diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java index eef809c720e..8c4de96db82 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java @@ -55,6 +55,7 @@ import mage.client.cards.BigCard; import mage.client.dialog.PreferencesDialog; import static mage.client.dialog.PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS; import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT; +import static mage.client.dialog.PreferencesDialog.KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE; import mage.constants.PlayerAction; import mage.view.PlayerView; @@ -72,7 +73,8 @@ public class PlayAreaPanel extends javax.swing.JPanel { private final GamePanel gamePanel; private final PlayAreaPanelOptions options; - private JCheckBoxMenuItem manaPoolMenuItem; + private JCheckBoxMenuItem manaPoolMenuItem1; + private JCheckBoxMenuItem manaPoolMenuItem2; private JCheckBoxMenuItem allowViewHandCardsMenuItem; public static final int PANEL_HEIGHT = 242; @@ -139,99 +141,135 @@ public class PlayAreaPanel extends javax.swing.JPanel { JMenuItem menuItem; - menuItem = new JMenuItem("F2 - Confirm"); - popupMenu.add(menuItem); - - // Confirm (F2) - menuItem.addActionListener(new ActionListener() { + ActionListener skipListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - if (gamePanel.getFeedbackPanel() != null) { - gamePanel.getFeedbackPanel().pressOKYesOrDone(); + switch (e.getActionCommand()) { + case "F2": { + if (gamePanel.getFeedbackPanel() != null) { + gamePanel.getFeedbackPanel().pressOKYesOrDone(); + } + break; + } + case "F3": { + gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_CANCEL_ALL_ACTIONS, gameId, null); + break; + } + case "F4": { + gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_NEXT_TURN, gameId, null); + break; + } + case "F5": { + gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_TURN_END_STEP, gameId, null); + break; + } + case "F7": { + gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_NEXT_MAIN_PHASE, gameId, null); + break; + } + case "F9": { + gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_MY_NEXT_TURN, gameId, null); + break; + } } } - }); - - - menuItem = new JMenuItem("F3 - Cancel previous F4/F9 skip action"); + }; + + menuItem = new JMenuItem("F2 - Confirm current request"); + menuItem.setActionCommand("F2"); + menuItem.setMnemonic(KeyEvent.VK_O); popupMenu.add(menuItem); + menuItem.addActionListener(skipListener); - // Cancel (F3) - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_CANCEL_ALL_ACTIONS, gameId, null); - } - }); + menuItem = new JMenuItem("F3 - Cancel active skip action"); + menuItem.setActionCommand("F3"); + menuItem.setMnemonic(KeyEvent.VK_N); + popupMenu.add(menuItem); + menuItem.addActionListener(skipListener); + + + JMenu skipMenu = new JMenu("Skip"); + skipMenu.setMnemonic(KeyEvent.VK_S); + popupMenu.add(skipMenu); + + String tooltipText = "This skip actions stops if something goes to
stack and if attackers or blocker have to be declared."; + menuItem = new JMenuItem("F4 - Phases until next turn"); + menuItem.setActionCommand("F4"); + menuItem.setToolTipText(tooltipText); + menuItem.setMnemonic(KeyEvent.VK_T); + skipMenu.add(menuItem); + menuItem.addActionListener(skipListener); + + menuItem = new JMenuItem("F5 - Phases until next end step"); + menuItem.setActionCommand("F5"); + menuItem.setToolTipText(tooltipText); + menuItem.setMnemonic(KeyEvent.VK_E); + skipMenu.add(menuItem); + menuItem.addActionListener(skipListener); + + menuItem = new JMenuItem("F7 - Phases until begin of next main phase"); + menuItem.setToolTipText(tooltipText); + menuItem.setActionCommand("F7"); + menuItem.setMnemonic(KeyEvent.VK_M); + skipMenu.add(menuItem); + menuItem.addActionListener(skipListener); + + menuItem = new JMenuItem("F9 - Everything until your own next turn"); + menuItem.setActionCommand("F9"); + menuItem.setToolTipText(tooltipText); + menuItem.setMnemonic(KeyEvent.VK_N); + skipMenu.add(menuItem); + menuItem.addActionListener(skipListener); popupMenu.addSeparator(); - - menuItem = new JMenuItem("F4 - Skip phases until next turn (stop on stack/attack/block)"); - popupMenu.add(menuItem); - - // Skip to next turn (F4) - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_NEXT_TURN, gameId, null); - } - }); - - menuItem = new JMenuItem("F5 - Skip phases until next end step (stop on stack/attack/block)"); - popupMenu.add(menuItem); - - // Skip to next end step of turn (F5) - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_TURN_END_STEP, gameId, null); - } - }); - - menuItem = new JMenuItem("F7 - Skip phases until begin of next main phase (stop on stack/attack/block)"); - popupMenu.add(menuItem); - - // Skip to next main phase (F7) - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_NEXT_MAIN_PHASE, gameId, null); - } - }); - menuItem = new JMenuItem("F9 - Skip everything until own next turn (stop on attack/block)"); - popupMenu.add(menuItem); - - // Skip to next own turn (F9) - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - gamePanel.getSession().sendPlayerAction(PlayerAction.PASS_PRIORITY_UNTIL_MY_NEXT_TURN, gameId, null); - } - }); - - popupMenu.addSeparator(); - - manaPoolMenuItem = new JCheckBoxMenuItem("Use mana from pool automatically", true); - manaPoolMenuItem.setMnemonic(KeyEvent.VK_M); - manaPoolMenuItem.setToolTipText("If not active, you have to click the type of mana you want to pay in the player panel."); - popupMenu.add(manaPoolMenuItem); + + JMenu manaPoolMenu = new JMenu("Mana payment"); + manaPoolMenu.setMnemonic(KeyEvent.VK_M); + popupMenu.add(manaPoolMenu); + + manaPoolMenuItem1 = new JCheckBoxMenuItem("Automatically", true); + manaPoolMenuItem1.setMnemonic(KeyEvent.VK_A); + manaPoolMenuItem1.setToolTipText("If not active, produced mana goes only to the mana pool
" + + "and you have to click the type of mana you want to use
" + + "in the player mana pool panel for payment."); + manaPoolMenu.add(manaPoolMenuItem1); // Auto pay mana from mana pool - manaPoolMenuItem.addActionListener(new ActionListener() { + manaPoolMenuItem1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { boolean manaPoolAutomatic = ((JCheckBoxMenuItem)e.getSource()).getState(); PreferencesDialog.saveValue(KEY_GAME_MANA_AUTOPAYMENT, manaPoolAutomatic ? "true": "false"); - gamePanel.setMenuStates(manaPoolAutomatic); + gamePanel.setMenuStates(manaPoolAutomatic, manaPoolMenuItem2.getState()); gamePanel.getSession().sendPlayerAction(manaPoolAutomatic ? PlayerAction.MANA_AUTO_PAYMENT_ON: PlayerAction.MANA_AUTO_PAYMENT_OFF, gameId, null); } }); + manaPoolMenuItem2 = new JCheckBoxMenuItem("No automatic usage for mana already in the pool", true); + manaPoolMenuItem2.setMnemonic(KeyEvent.VK_N); + manaPoolMenuItem2.setToolTipText("Mana that is already in the mana pool as you start casting a spell or activating an ability
" + + " needs to be payed manually. So you use the mana in the pool only by clicking on the related
" + + " mana symbols of mana pool area."); + manaPoolMenu.add(manaPoolMenuItem2); + // Auto pay mana from mana pool + manaPoolMenuItem2.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + boolean manaPoolAutomaticRestricted = ((JCheckBoxMenuItem)e.getSource()).getState(); + PreferencesDialog.saveValue(KEY_GAME_MANA_AUTOPAYMENT_ONLY_ONE, manaPoolAutomaticRestricted ? "true": "false"); + gamePanel.setMenuStates(manaPoolMenuItem1.getState(), manaPoolAutomaticRestricted); + gamePanel.getSession().sendPlayerAction(manaPoolAutomaticRestricted ? PlayerAction.MANA_AUTO_PAYMENT_RESTRICTED_ON: PlayerAction.MANA_AUTO_PAYMENT_RESTRICTED_OFF, gameId, null); + } + }); + JMenu automaticConfirmsMenu = new JMenu("Automatic confirms"); + automaticConfirmsMenu.setMnemonic(KeyEvent.VK_U); + popupMenu.add(automaticConfirmsMenu); + menuItem = new JMenuItem("Replacement effects - reset auto select"); menuItem.setMnemonic(KeyEvent.VK_R); menuItem.setToolTipText("Reset all effects that were added to the list of auto select replacement effects this game."); - popupMenu.add(menuItem); + automaticConfirmsMenu.add(menuItem); // Reset the replacement effcts that were auto selected for the game menuItem.addActionListener(new ActionListener() { @Override @@ -240,11 +278,14 @@ public class PlayAreaPanel extends javax.swing.JPanel { } }); - popupMenu.addSeparator(); - + JMenu handCardsMenu = new JMenu("Cards on hand"); + handCardsMenu.setMnemonic(KeyEvent.VK_H); + popupMenu.add(handCardsMenu); + if (!options.playerItself) { - menuItem = new JMenuItem("Request permission to see hand cards"); - popupMenu.add(menuItem); + menuItem = new JMenuItem("Request permission to see the hand cards"); + menuItem.setMnemonic(KeyEvent.VK_P); + handCardsMenu.add(menuItem); // Request to see hand cards menuItem.addActionListener(new ActionListener() { @@ -254,10 +295,10 @@ public class PlayAreaPanel extends javax.swing.JPanel { } }); } else { - allowViewHandCardsMenuItem = new JCheckBoxMenuItem("Allow requests to show your hand cards", allowRequestToShowHandCards); + allowViewHandCardsMenuItem = new JCheckBoxMenuItem("Allow requests to show from other users", allowRequestToShowHandCards); allowViewHandCardsMenuItem.setMnemonic(KeyEvent.VK_A); allowViewHandCardsMenuItem.setToolTipText("If activated watchers or other players can request to see your hand cards. If you grant this to a user, it's valid for the complete match."); - popupMenu.add(allowViewHandCardsMenuItem); + handCardsMenu.add(allowViewHandCardsMenuItem); // Requests allowed allowViewHandCardsMenuItem.addActionListener(new ActionListener() { @@ -270,9 +311,9 @@ public class PlayAreaPanel extends javax.swing.JPanel { }); menuItem = new JMenuItem("Revoke all permission(s) to see your hand cards"); - menuItem.setMnemonic(KeyEvent.VK_P); + menuItem.setMnemonic(KeyEvent.VK_R); menuItem.setToolTipText("Revoke already granted permission for all spectators to see your hand cards."); - popupMenu.add(menuItem); + handCardsMenu.add(menuItem); // revoke permissions to see hand cards menuItem.addActionListener(new ActionListener() { @@ -282,8 +323,7 @@ public class PlayAreaPanel extends javax.swing.JPanel { } }); } - popupMenu.addSeparator(); - + if (options.rollbackTurnsAllowed) { ActionListener rollBackActionListener = new ActionListener() { @Override @@ -298,78 +338,73 @@ public class PlayAreaPanel extends javax.swing.JPanel { rollbackMainItem.setToolTipText("The game will be rolled back to the start of the requested turn if all players agree."); popupMenu.add(rollbackMainItem); - menuItem = new JMenuItem("to the start of the current turn"); + menuItem = new JMenuItem("To the start of the current turn"); menuItem.setMnemonic(KeyEvent.VK_C); menuItem.setActionCommand("0"); menuItem.addActionListener(rollBackActionListener); rollbackMainItem.add(menuItem); - menuItem = new JMenuItem("to the start of the previous turn"); + menuItem = new JMenuItem("To the start of the previous turn"); menuItem.setMnemonic(KeyEvent.VK_P); menuItem.setActionCommand("1"); menuItem.addActionListener(rollBackActionListener); rollbackMainItem.add(menuItem); - menuItem = new JMenuItem("the current turn and the 2 turns before"); + menuItem = new JMenuItem("The current turn and the 2 turns before"); menuItem.setMnemonic(KeyEvent.VK_2); menuItem.setActionCommand("2"); menuItem.addActionListener(rollBackActionListener); rollbackMainItem.add(menuItem); - menuItem = new JMenuItem("the current turn and the 3 turns before"); + menuItem = new JMenuItem("The current turn and the 3 turns before"); menuItem.setMnemonic(KeyEvent.VK_3); menuItem.setActionCommand("3"); menuItem.addActionListener(rollBackActionListener); rollbackMainItem.add(menuItem); + + } - popupMenu.addSeparator(); - - } - - menuItem = new JMenuItem("Revoke all permission(s) to see your hand cards"); - menuItem.setMnemonic(KeyEvent.VK_P); - menuItem.setToolTipText("Revoke already granted permission for all spectators to see your hand cards."); - popupMenu.add(menuItem); + - // revoke permissions to see hand cards - menuItem.addActionListener(new ActionListener() { + JMenu concedeMenu = new JMenu("Concede"); + concedeMenu.setMnemonic(KeyEvent.VK_C); + popupMenu.add(concedeMenu); + + ActionListener concedeListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - gamePanel.getSession().sendPlayerAction(PlayerAction.REVOKE_PERMISSIONS_TO_SEE_HAND_CARDS, gameId, null); - } - }); - - popupMenu.addSeparator(); - - menuItem = new JMenuItem("Concede game"); - popupMenu.add(menuItem); - - // Concede - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (JOptionPane.showConfirmDialog(PlayAreaPanel.this, "Are you sure you want to concede the game?", "Confirm concede game", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - MageFrame.getSession().sendPlayerAction(PlayerAction.CONCEDE, gameId, null); + switch (e.getActionCommand()) { + case "Game": { + if (JOptionPane.showConfirmDialog(PlayAreaPanel.this, "Are you sure you want to concede the game?", "Confirm concede game", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + MageFrame.getSession().sendPlayerAction(PlayerAction.CONCEDE, gameId, null); + } + } + case "Match": { + if (JOptionPane.showConfirmDialog(PlayAreaPanel.this, "Are you sure you want to concede the complete match?", "Confirm concede match", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { + MageFrame.getSession().quitMatch(gameId); + } + } } } - }); - - popupMenu.addSeparator(); - - menuItem = new JMenuItem("Concede complete match"); - popupMenu.add(menuItem); - - // Quit match - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (JOptionPane.showConfirmDialog(PlayAreaPanel.this, "Are you sure you want to concede the complete match?", "Confirm concede match", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { - MageFrame.getSession().quitMatch(gameId); - } - } - }); - + }; + + // Concede Game + menuItem = new JMenuItem("Game"); + menuItem.setMnemonic(KeyEvent.VK_G); + menuItem.setActionCommand("Game"); + menuItem.setToolTipText("Concedes only the current game and after that the next game of the match is started if there is another game needed."); + concedeMenu.add(menuItem); + menuItem.addActionListener(concedeListener); + + // Concede Match + menuItem = new JMenuItem("Match"); + menuItem.setMnemonic(KeyEvent.VK_M); + menuItem.setActionCommand("Match"); + menuItem.setToolTipText("Concedes the complete match. So if you're in a tournament you finish the current tournament round."); + concedeMenu.add(menuItem); + menuItem.addActionListener(concedeListener); + battlefieldPanel.getMainPanel().addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent Me) { @@ -531,8 +566,9 @@ public class PlayAreaPanel extends javax.swing.JPanel { this.playingMode = playingMode; } - public void setMenuStates(boolean manaPoolAutomatic) { - manaPoolMenuItem.setSelected(manaPoolAutomatic); + public void setMenuStates(boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted) { + manaPoolMenuItem1.setSelected(manaPoolAutomatic); + manaPoolMenuItem2.setSelected(manaPoolAutomaticRestricted); } private mage.client.game.BattlefieldPanel battlefieldPanel; diff --git a/Mage.Common/src/mage/remote/interfaces/GamePlay.java b/Mage.Common/src/mage/remote/interfaces/GamePlay.java index d99f17011cb..f54a193fd31 100644 --- a/Mage.Common/src/mage/remote/interfaces/GamePlay.java +++ b/Mage.Common/src/mage/remote/interfaces/GamePlay.java @@ -78,6 +78,7 @@ public interface GamePlay { * * @param passPriorityAction * @param gameId + * @param Data * @return */ boolean sendPlayerAction(PlayerAction passPriorityAction, UUID gameId, Object Data); diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index da30ce7fde6..5f541cc186b 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -537,10 +537,16 @@ public class GameController implements GameCallback { game.concede(getPlayerId(userId)); break; case MANA_AUTO_PAYMENT_OFF: - game.setManaPoolMode(getPlayerId(userId), false); + game.setManaPaymentMode(getPlayerId(userId), false); break; case MANA_AUTO_PAYMENT_ON: - game.setManaPoolMode(getPlayerId(userId), true); + game.setManaPaymentMode(getPlayerId(userId), true); + break; + case MANA_AUTO_PAYMENT_RESTRICTED_OFF: + game.setManaPaymentModeRestricted(getPlayerId(userId), false); + break; + case MANA_AUTO_PAYMENT_RESTRICTED_ON: + game.setManaPaymentModeRestricted(getPlayerId(userId), true); break; case ADD_PERMISSION_TO_SEE_HAND_CARDS: if (data instanceof UUID) { diff --git a/Mage/src/mage/constants/PlayerAction.java b/Mage/src/mage/constants/PlayerAction.java index 5194b113033..82c18616d72 100644 --- a/Mage/src/mage/constants/PlayerAction.java +++ b/Mage/src/mage/constants/PlayerAction.java @@ -44,6 +44,8 @@ public enum PlayerAction { CONCEDE, MANA_AUTO_PAYMENT_ON, MANA_AUTO_PAYMENT_OFF, + MANA_AUTO_PAYMENT_RESTRICTED_ON, + MANA_AUTO_PAYMENT_RESTRICTED_OFF, RESET_AUTO_SELECT_REPLACEMENT_EFFECTS, REVOKE_PERMISSIONS_TO_SEE_HAND_CARDS, REQUEST_PERMISSION_TO_SEE_HAND_CARDS, diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 6429c34a0ab..3743aae59c4 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -224,7 +224,8 @@ public interface Game extends MageItem, Serializable { void timerTimeout(UUID playerId); void idleTimeout(UUID playerId); void concede(UUID playerId); - void setManaPoolMode(UUID playerId, boolean autoPayment); + void setManaPaymentMode(UUID playerId, boolean autoPayment); + void setManaPaymentModeRestricted(UUID playerId, boolean autoPaymentRestricted); void undo(UUID playerId); void emptyManaPools(); void addEffect(ContinuousEffect continuousEffect, Ability source); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 991c93727c5..8a108c50aad 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -1124,12 +1124,20 @@ public abstract class GameImpl implements Game, Serializable { @Override - public synchronized void setManaPoolMode(UUID playerId, boolean autoPayment) { + public synchronized void setManaPaymentMode(UUID playerId, boolean autoPayment) { Player player = state.getPlayer(playerId); if (player != null) { player.getManaPool().setAutoPayment(autoPayment); } } + + @Override + public synchronized void setManaPaymentModeRestricted(UUID playerId, boolean autoPaymentRestricted) { + Player player = state.getPlayer(playerId); + if (player != null) { + player.getManaPool().setAutoPaymentRestricted(autoPaymentRestricted); + } + } @Override public void playPriority(UUID activePlayerId, boolean resuming) { diff --git a/Mage/src/mage/players/ManaPool.java b/Mage/src/mage/players/ManaPool.java index 3e50bbf9c24..200faae6b79 100644 --- a/Mage/src/mage/players/ManaPool.java +++ b/Mage/src/mage/players/ManaPool.java @@ -61,6 +61,7 @@ public class ManaPool implements Serializable { private final List manaItems = new ArrayList<>(); private boolean autoPayment; // auto payment from mana pool: true - mode is active + private boolean autoPaymentRestricted; // auto payment from mana pool: true - if auto Payment is on, it will only pay if one kind of mana is in the pool private ManaType unlockedManaType; // type of mana that was selected to pay manually private final Set doNotEmptyManaTypes = new HashSet<>(); @@ -68,6 +69,7 @@ public class ManaPool implements Serializable { public ManaPool(UUID playerId) { this.playerId = playerId; autoPayment = true; + autoPaymentRestricted = true; unlockedManaType = null; } @@ -77,6 +79,7 @@ public class ManaPool implements Serializable { manaItems.add(item.copy()); } this.autoPayment = pool.autoPayment; + this.autoPaymentRestricted = pool.autoPaymentRestricted; this.unlockedManaType = pool.unlockedManaType; this.doNotEmptyManaTypes.addAll(pool.doNotEmptyManaTypes); } @@ -101,11 +104,25 @@ public class ManaPool implements Serializable { return get(ManaType.BLACK); } + /** + * + * @param manaType the mana type that should be paid + * @param ability + * @param filter + * @param game + * @return + */ public boolean pay(ManaType manaType, Ability ability, Filter filter, Game game) { if (!autoPayment && !manaType.equals(unlockedManaType)) { // if manual payment and the needed mana type was not unlocked, nothing will be paid return false; } + if (autoPayment && autoPaymentRestricted && !wasManaAddedBeyondStock() && !manaType.equals(unlockedManaType)) { + // if automatic restricted payment and there is laready mana in the pool + // and the needed mana type was not unlocked, nothing will be paid + return false; + } + if (getConditional(manaType, ability, filter, game) > 0) { removeConditional(manaType, ability, game); lockManaType(); // pay only one mana if mana payment is set to manually @@ -118,6 +135,10 @@ public class ManaPool implements Serializable { continue; } } + if (!manaType.equals(unlockedManaType) && autoPayment && autoPaymentRestricted && mana.count() == mana.getStock()) { + // no mana added beyond the stock so don't auto pay this + continue; + } boolean spendAnyMana = spendAnyMana(ability, game); if (mana.get(manaType) > 0 || (spendAnyMana && mana.count() > 0)) { GameEvent event = new GameEvent(GameEvent.EventType.MANA_PAYED, ability.getId(), mana.getSourceId(), ability.getControllerId(), 0, mana.getFlag()); @@ -128,6 +149,9 @@ public class ManaPool implements Serializable { } else { mana.remove(manaType); } + if (mana.count() == 0) { // so no items with count 0 stay in list + manaItems.remove(mana); + } lockManaType(); // pay only one mana if mana payment is set to manually return true; } @@ -416,6 +440,14 @@ public class ManaPool implements Serializable { this.autoPayment = autoPayment; } + public void setAutoPaymentRestricted(boolean autoPaymentRestricted) { + this.autoPaymentRestricted = autoPaymentRestricted; + } + + public boolean isAutoPaymentRestricted() { + return autoPaymentRestricted; + } + public ManaType getUnlockedManaType() { return unlockedManaType; } @@ -428,4 +460,18 @@ public class ManaPool implements Serializable { this.unlockedManaType = null; } + public void setStock() { + for (ManaPoolItem mana : manaItems) { + mana.setStock(mana.count()); + } + } + + private boolean wasManaAddedBeyondStock() { + for (ManaPoolItem mana : manaItems) { + if (mana.getStock() < mana.count()) { + return true; + } + } + return false; + } } diff --git a/Mage/src/mage/players/ManaPoolItem.java b/Mage/src/mage/players/ManaPoolItem.java index 0d952cb41cd..b6a8f796b9a 100644 --- a/Mage/src/mage/players/ManaPoolItem.java +++ b/Mage/src/mage/players/ManaPoolItem.java @@ -51,6 +51,7 @@ public class ManaPoolItem implements Serializable { private UUID originalId; // originalId of the mana producing ability private boolean flag = false; private Duration duration; + private int stock; // amount the item had at the start of casting something public ManaPoolItem() {} @@ -91,6 +92,7 @@ public class ManaPoolItem implements Serializable { this.originalId = item.originalId; this.flag = item.flag; this.duration = item.duration; + this.stock = item.stock; } public ManaPoolItem copy() { @@ -207,8 +209,9 @@ public class ManaPoolItem implements Serializable { } public void removeAny() { + int oldCount = count(); if (black > 0) { - black--; + black--; } else if (blue > 0) { blue--; } else if (green > 0) { @@ -219,10 +222,14 @@ public class ManaPoolItem implements Serializable { white--; } else if (colorless > 0) { colorless--; - } + } + if (stock == oldCount && oldCount > count()) { + stock--; + } } public void remove(ManaType manaType) { + int oldCount = count(); switch(manaType) { case BLACK: if (black > 0) { @@ -255,6 +262,9 @@ public class ManaPoolItem implements Serializable { } break; } + if (stock == oldCount && oldCount > count()) { + stock--; + } } public void clear(ManaType manaType) { @@ -310,5 +320,13 @@ public class ManaPoolItem implements Serializable { public void setDuration(Duration duration) { this.duration = duration; } + + public int getStock() { + return stock; + } + + public void setStock(int stock) { + this.stock = stock; + } } diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index da437f09435..1d3bd46276f 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1085,6 +1085,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean activateAbility(ActivatedAbility ability, Game game) { + getManaPool().setStock(); // needed for the "mana already in the pool has to be used manually" option boolean result; if (ability instanceof PassAbility) { pass(game); From 9da44a547df5c851c7673ec87fc0213d88a69d6b Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 7 Jun 2015 22:37:12 +0200 Subject: [PATCH 076/102] Added an option that player has to confirm if he passes priority with empty stack and has mana in his pool. Implements #707. --- .../mage/client/dialog/ConnectDialog.java | 1 + .../mage/client/dialog/PreferencesDialog.form | 24 ++++- .../mage/client/dialog/PreferencesDialog.java | 36 +++++-- .../java/mage/client/game/HelperPanel.java | 18 ++-- .../java/mage/client/game/PlayAreaPanel.java | 6 +- Mage.Common/src/mage/remote/Connection.java | 11 +++ Mage.Common/src/mage/remote/SessionImpl.java | 5 +- .../mage/remote/interfaces/ClientData.java | 2 +- Mage.Common/src/mage/view/PlayerView.java | 2 +- Mage.Common/src/mage/view/UserDataView.java | 10 +- .../java/mage/player/ai/ComputerPlayer.java | 2 +- .../src/mage/player/human/HumanPlayer.java | 97 +++++++++++++------ .../src/main/java/mage/server/Session.java | 7 +- Mage/src/mage/players/PlayerImpl.java | 5 +- Mage/src/mage/players/net/UserData.java | 13 ++- Mage/src/mage/util/GameLog.java | 9 ++ 16 files changed, 187 insertions(+), 61 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index 4c72ed3535e..a92c7a937fb 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -335,6 +335,7 @@ public class ConnectDialog extends MageDialog { connection.setAllowRequestShowHandCards(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true").equals("true")); connection.setShowAbilityPickerForced(showAbilityPickerForced); connection.setUserSkipPrioritySteps(PreferencesDialog.getUserSkipPrioritySteps()); + connection.setConfirmEmptyManaPool(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true").equals("true")); logger.debug("connecting: " + connection.getProxyType() + " " + connection.getProxyHost() + " " + connection.getProxyPort()); task = new ConnectTask(); task.execute(); diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form index 024fe542240..3a64ad1f596 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form @@ -31,7 +31,7 @@ - + @@ -85,7 +85,7 @@ - + @@ -203,16 +203,17 @@ - + - + + @@ -232,6 +233,8 @@ + + @@ -293,6 +296,17 @@ + + + + + + + + + + +
@@ -1128,7 +1142,7 @@ - + diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 5221a550519..c1775a462ac 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -85,6 +85,7 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_SHOW_ABILITY_PICKER_FORCED = "showAbilityPicker"; public static final String KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS = "gameAllowRequestShowHandCards"; public static final String KEY_GAME_SHOW_STORM_COUNTER = "gameShowStormCounter"; + public static final String KEY_GAME_CONFIRM_EMPTY_MANA_POOL = "gameConfirmEmptyManaPool"; public static final String KEY_GAME_LOG_AUTO_SAVE = "gameLogAutoSave"; @@ -335,6 +336,7 @@ public class PreferencesDialog extends javax.swing.JDialog { showAbilityPickerForced = new javax.swing.JCheckBox(); cbAllowRequestToShowHandCards = new javax.swing.JCheckBox(); cbShowStormCounter = new javax.swing.JCheckBox(); + cbConfirmEmptyManaPool = new javax.swing.JCheckBox(); main_gamelog = new javax.swing.JPanel(); cbGameLogAutoSave = new javax.swing.JCheckBox(); tabPhases = new javax.swing.JPanel(); @@ -549,6 +551,16 @@ public class PreferencesDialog extends javax.swing.JDialog { } }); + cbConfirmEmptyManaPool.setSelected(true); + cbConfirmEmptyManaPool.setText("Confirm if you want to pass a phase/step but there is still mana in your mana pool"); + cbConfirmEmptyManaPool.setToolTipText("If activated you get a confirm message if you pass priority while stack is empty
\n and you still have mana in your mana pool."); + cbConfirmEmptyManaPool.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); + cbConfirmEmptyManaPool.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cbConfirmEmptyManaPoolActionPerformed(evt); + } + }); + javax.swing.GroupLayout main_gameLayout = new javax.swing.GroupLayout(main_game); main_game.setLayout(main_gameLayout); main_gameLayout.setHorizontalGroup( @@ -558,14 +570,15 @@ public class PreferencesDialog extends javax.swing.JDialog { .addGroup(main_gameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(main_gameLayout.createSequentialGroup() .addComponent(cbAllowRequestToShowHandCards, javax.swing.GroupLayout.PREFERRED_SIZE, 546, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(14, Short.MAX_VALUE)) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(main_gameLayout.createSequentialGroup() .addGroup(main_gameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(main_gameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addComponent(showPlayerNamesPermanently, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(nonLandPermanentsInOnePile, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(showAbilityPickerForced, javax.swing.GroupLayout.Alignment.LEADING)) - .addComponent(cbShowStormCounter, javax.swing.GroupLayout.PREFERRED_SIZE, 546, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addComponent(cbShowStormCounter, javax.swing.GroupLayout.PREFERRED_SIZE, 546, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(cbConfirmEmptyManaPool, javax.swing.GroupLayout.PREFERRED_SIZE, 546, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGap(0, 0, Short.MAX_VALUE)))) ); main_gameLayout.setVerticalGroup( @@ -579,7 +592,9 @@ public class PreferencesDialog extends javax.swing.JDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(cbAllowRequestToShowHandCards) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cbShowStormCounter)) + .addComponent(cbShowStormCounter) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cbConfirmEmptyManaPool)) ); nonLandPermanentsInOnePile.getAccessibleContext().setAccessibleName("nonLandPermanentsInOnePile"); @@ -630,7 +645,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .addComponent(main_game, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(main_gamelog, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(63, Short.MAX_VALUE)) + .addContainerGap(40, Short.MAX_VALUE)) ); main_card.getAccessibleContext().setAccessibleName("Game panel"); @@ -1383,7 +1398,7 @@ public class PreferencesDialog extends javax.swing.JDialog { tabAvatars.setLayout(tabAvatarsLayout); tabAvatarsLayout.setHorizontalGroup( tabAvatarsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 598, Short.MAX_VALUE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 590, Short.MAX_VALUE) ); tabAvatarsLayout.setVerticalGroup( tabAvatarsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1602,7 +1617,7 @@ public class PreferencesDialog extends javax.swing.JDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(exitButton, javax.swing.GroupLayout.PREFERRED_SIZE, 55, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap()) - .addComponent(tabsPanel) + .addComponent(tabsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 595, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -1630,6 +1645,7 @@ public class PreferencesDialog extends javax.swing.JDialog { save(prefs, dialog.showAbilityPickerForced, KEY_SHOW_ABILITY_PICKER_FORCED, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbAllowRequestToShowHandCards, KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbShowStormCounter, KEY_GAME_SHOW_STORM_COUNTER, "true", "false", UPDATE_CACHE_POLICY); + save(prefs, dialog.cbConfirmEmptyManaPool, KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true", "false", UPDATE_CACHE_POLICY); save(prefs, dialog.cbGameLogAutoSave, KEY_GAME_LOG_AUTO_SAVE, "true", "false", UPDATE_CACHE_POLICY); // Phases @@ -1696,6 +1712,7 @@ public class PreferencesDialog extends javax.swing.JDialog { getSelectedAvatar(), dialog.showAbilityPickerForced.isSelected(), dialog.cbAllowRequestToShowHandCards.isSelected(), + dialog.cbConfirmEmptyManaPool.isSelected(), getUserSkipPrioritySteps()); prefs.flush(); @@ -1947,6 +1964,10 @@ public class PreferencesDialog extends javax.swing.JDialog { // TODO add your handling code here: }//GEN-LAST:event_cbShowStormCounterActionPerformed + private void cbConfirmEmptyManaPoolActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbConfirmEmptyManaPoolActionPerformed + // TODO add your handling code here: + }//GEN-LAST:event_cbConfirmEmptyManaPoolActionPerformed + private void showProxySettings() { if (cbProxyType.getSelectedItem() == Connection.ProxyType.SOCKS) { this.pnlProxy.setVisible(true); @@ -2023,6 +2044,7 @@ public class PreferencesDialog extends javax.swing.JDialog { load(prefs, dialog.showAbilityPickerForced, KEY_SHOW_ABILITY_PICKER_FORCED, "true"); load(prefs, dialog.cbAllowRequestToShowHandCards, KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true"); load(prefs, dialog.cbShowStormCounter, KEY_GAME_SHOW_STORM_COUNTER, "true"); + load(prefs, dialog.cbConfirmEmptyManaPool, KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true"); load(prefs, dialog.cbGameLogAutoSave, KEY_GAME_LOG_AUTO_SAVE, "true"); @@ -2367,6 +2389,7 @@ public class PreferencesDialog extends javax.swing.JDialog { id, PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_TOOLTIPS_ANY_ZONE, "true").equals("true"), PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true").equals("true"), + PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true").equals("true"), getUserSkipPrioritySteps()); } } @@ -2381,6 +2404,7 @@ public class PreferencesDialog extends javax.swing.JDialog { private javax.swing.JButton btnBrowseImageLocation; private javax.swing.JCheckBox cbAllowRequestToShowHandCards; private javax.swing.JCheckBox cbCheckForNewImages; + private javax.swing.JCheckBox cbConfirmEmptyManaPool; private javax.swing.JCheckBox cbEnableBattlefieldBGM; private javax.swing.JCheckBox cbEnableDraftSounds; private javax.swing.JCheckBox cbEnableGameSounds; diff --git a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java index b2e2a1f2860..5f325d1f98b 100644 --- a/Mage.Client/src/main/java/mage/client/game/HelperPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/HelperPanel.java @@ -221,12 +221,18 @@ public class HelperPanel extends JPanel { public void setMessage(String message) { if (message.startsWith("Use alternative cost")) { - textArea.setText("Use alternative cost?"); - } else if (message.length() > 40 && message.contains("Use ")) { - textArea.setText("Use ability?"); - } else { - textArea.setText(message); - } + message = "Use alternative cost?"; + } else if (message.contains("Use ")) { + if (message.length() < this.getWidth() / 10) { + message = getSmallText(message); + } else { + message = "Use ability?" + getSmallText(message.substring(this.getWidth() / 10)); + } + } + textArea.setText(message); + } + protected String getSmallText(String text) { + return "
" + text + "
"; } @Override diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java index 8c4de96db82..4ffc7cd5f85 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java @@ -377,12 +377,14 @@ public class PlayAreaPanel extends javax.swing.JPanel { case "Game": { if (JOptionPane.showConfirmDialog(PlayAreaPanel.this, "Are you sure you want to concede the game?", "Confirm concede game", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { MageFrame.getSession().sendPlayerAction(PlayerAction.CONCEDE, gameId, null); - } + } + break; } case "Match": { if (JOptionPane.showConfirmDialog(PlayAreaPanel.this, "Are you sure you want to concede the complete match?", "Confirm concede match", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { MageFrame.getSession().quitMatch(gameId); - } + } + break; } } } diff --git a/Mage.Common/src/mage/remote/Connection.java b/Mage.Common/src/mage/remote/Connection.java index b93e28ebd3c..1ce44b0ef77 100644 --- a/Mage.Common/src/mage/remote/Connection.java +++ b/Mage.Common/src/mage/remote/Connection.java @@ -57,6 +57,7 @@ public class Connection { private int avatarId; private boolean showAbilityPickerForced; private boolean allowRequestShowHandCards; + private boolean confirmEmptyManaPool; private UserSkipPrioritySteps userSkipPrioritySteps; private static final String serialization = "?serializationtype=jboss"; @@ -242,6 +243,16 @@ public class Connection { public void setAllowRequestShowHandCards(boolean allowRequestShowHandCards) { this.allowRequestShowHandCards = allowRequestShowHandCards; } + + public boolean confirmEmptyManaPool() { + return confirmEmptyManaPool; + } + + public void setConfirmEmptyManaPool(boolean confirmEmptyManaPool) { + this.confirmEmptyManaPool = confirmEmptyManaPool; + } + + public UserSkipPrioritySteps getUserSkipPrioritySteps() { return userSkipPrioritySteps; } diff --git a/Mage.Common/src/mage/remote/SessionImpl.java b/Mage.Common/src/mage/remote/SessionImpl.java index 3ae7e622083..702857db496 100644 --- a/Mage.Common/src/mage/remote/SessionImpl.java +++ b/Mage.Common/src/mage/remote/SessionImpl.java @@ -278,6 +278,7 @@ public class SessionImpl implements Session { UserDataView userDataView = new UserDataView(connection.getAvatarId(), connection.isShowAbilityPickerForced(), connection.allowRequestShowHandCards(), + connection.confirmEmptyManaPool(), connection.getUserSkipPrioritySteps()); // for backward compatibility. don't remove twice call - first one does nothing but for version checking registerResult = server.registerClient(connection.getUsername(), sessionId, client.getVersion()); @@ -1395,10 +1396,10 @@ public class SessionImpl implements Session { } @Override - public boolean updatePreferencesForServer(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, UserSkipPrioritySteps userSkipPrioritySteps) { + public boolean updatePreferencesForServer(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps) { try { if (isConnected()) { - UserDataView userDataView = new UserDataView(avatarId, showAbilityPickerForced, allowRequestShowHandCards, userSkipPrioritySteps); + UserDataView userDataView = new UserDataView(avatarId, showAbilityPickerForced, allowRequestShowHandCards, confirmEmptyManaPool, userSkipPrioritySteps); server.setUserData(connection.getUsername(), sessionId, userDataView); } return true; diff --git a/Mage.Common/src/mage/remote/interfaces/ClientData.java b/Mage.Common/src/mage/remote/interfaces/ClientData.java index e2cc74ff1ff..64242130e99 100644 --- a/Mage.Common/src/mage/remote/interfaces/ClientData.java +++ b/Mage.Common/src/mage/remote/interfaces/ClientData.java @@ -36,5 +36,5 @@ public interface ClientData { String getUserName(); - boolean updatePreferencesForServer(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, UserSkipPrioritySteps userSkipPrioritySteps); + boolean updatePreferencesForServer(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps); } diff --git a/Mage.Common/src/mage/view/PlayerView.java b/Mage.Common/src/mage/view/PlayerView.java index fb9e36ad62c..5ed57121f84 100644 --- a/Mage.Common/src/mage/view/PlayerView.java +++ b/Mage.Common/src/mage/view/PlayerView.java @@ -115,7 +115,7 @@ public class PlayerView implements Serializable { if (player.getUserData() != null) { this.userDataView = new UserDataView(player.getUserData()); } else { - this.userDataView = new UserDataView(0, false, false, null); + this.userDataView = new UserDataView(0, false, false, true, null); } for (CommandObject commandObject : game.getState().getCommand()) { diff --git a/Mage.Common/src/mage/view/UserDataView.java b/Mage.Common/src/mage/view/UserDataView.java index 48aed4d25c4..d1e17f3884c 100644 --- a/Mage.Common/src/mage/view/UserDataView.java +++ b/Mage.Common/src/mage/view/UserDataView.java @@ -15,13 +15,16 @@ public class UserDataView implements Serializable { protected int userGroup; protected boolean showAbilityPickerForced; protected boolean allowRequestShowHandCards; + protected boolean confirmEmptyManaPool; protected UserSkipPrioritySteps userSkipPrioritySteps; - public UserDataView(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, UserSkipPrioritySteps userSkipPrioritySteps) { + public UserDataView(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, + boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps) { this.avatarId = avatarId; this.showAbilityPickerForced = showAbilityPickerForced; this.allowRequestShowHandCards = allowRequestShowHandCards; this.userSkipPrioritySteps = userSkipPrioritySteps; + this.confirmEmptyManaPool = confirmEmptyManaPool; } public UserDataView(UserData userData) { @@ -30,6 +33,7 @@ public class UserDataView implements Serializable { this.allowRequestShowHandCards = userData.isAllowRequestShowHandCards(); this.showAbilityPickerForced = userData.isShowAbilityPickerForced(); this.userSkipPrioritySteps = userData.getUserSkipPrioritySteps(); + this.confirmEmptyManaPool = userData.confirmEmptyManaPool(); } public int getAvatarId() { @@ -47,5 +51,9 @@ public class UserDataView implements Serializable { public UserSkipPrioritySteps getUserSkipPrioritySteps() { return userSkipPrioritySteps; } + + public boolean confirmEmptyManaPool() { + return confirmEmptyManaPool; + } } diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 170b26a3ce3..8b8d0febd63 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -175,7 +175,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { public ComputerPlayer(String name, RangeOfInfluence range) { super(name, range); human = false; - userData = new UserData(UserGroup.COMPUTER, 64, false, true, null); + userData = new UserData(UserGroup.COMPUTER, 64, false, true, false, null); pickedCards = new ArrayList<>(); } diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 537b568f6ca..26308d76169 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -68,6 +68,7 @@ import org.apache.log4j.Logger; import java.io.Serializable; import java.util.*; +import mage.util.GameLog; /** * @@ -494,31 +495,35 @@ public class HumanPlayer extends PlayerImpl { passed = false; if (!abort) { if (passedAllTurns) { - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } if (game.getStack().isEmpty()) { passedUntilStackResolved = false; boolean dontCheckPassStep = false; if (passedTurn) { - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } if (passedUntilNextMain) { if (game.getTurn().getStepType().equals(PhaseStep.POSTCOMBAT_MAIN) || game.getTurn().getStepType().equals(PhaseStep.PRECOMBAT_MAIN)) { // it's a main phase if (!skippedAtLeastOnce || (!playerId.equals(game.getActivePlayerId()) && !this.getUserData().getUserSkipPrioritySteps().isStopOnAllMainPhases())) { skippedAtLeastOnce = true; - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } else { dontCheckPassStep = true; passedUntilNextMain = false; // reset skip action } } else { skippedAtLeastOnce = true; - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } } if (passedUntilEndOfTurn) { @@ -526,48 +531,53 @@ public class HumanPlayer extends PlayerImpl { // It's end of turn phase if (!skippedAtLeastOnce || (playerId.equals(game.getActivePlayerId()) && !this.getUserData().getUserSkipPrioritySteps().isStopOnAllEndPhases())) { skippedAtLeastOnce = true; - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } else { dontCheckPassStep = true; passedUntilEndOfTurn = false; } } else { skippedAtLeastOnce = true; - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } } if (!dontCheckPassStep && checkPassStep(game)) { - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } } else if (passedUntilStackResolved) { if (dateLastAddedToStack == game.getStack().getDateLastAdded()) { dateLastAddedToStack = game.getStack().getDateLastAdded(); - pass(game); - return false; + if(passWithManaPoolCheck(game)) { + return false; + } } else { passedUntilStackResolved = false; } } - updateGameStatePriority("priority", game); - game.firePriorityEvent(playerId); - waitForResponse(game); - if(game.executingRollback()) { - return true; + while (isInGame()) { + updateGameStatePriority("priority", game); + game.firePriorityEvent(playerId); + waitForResponse(game); + if(game.executingRollback()) { + return true; + } + if (response.getBoolean() != null || response.getInteger() != null) { + if (passWithManaPoolCheck(game)) { + return false; + } else { + continue; + } + } + break; } - if (response.getBoolean() != null) { - pass(game); - return false; - } else if (response.getInteger() != null) { - /*if (response.getInteger() == -9999) { - passedAllTurns = true; - }*/ - pass(game); - //passedTurn = true; - return false; - } else if (response.getString() != null && response.getString().equals("special")) { + + if (response.getString() != null && response.getString().equals("special")) { specialAction(game); } else if (response.getUUID() != null) { boolean result = false; @@ -1216,4 +1226,27 @@ public class HumanPlayer extends PlayerImpl { super.sendPlayerAction(playerAction, game); } } + + protected boolean passWithManaPoolCheck(Game game) { + if (userData.confirmEmptyManaPool() && + game.getStack().isEmpty() && getManaPool().count() > 0) { + String activePlayerText; + if (game.getActivePlayerId().equals(playerId)) { + activePlayerText = "Your turn"; + } else { + activePlayerText = game.getPlayer(game.getActivePlayerId()).getName() + "'s turn"; + } + String priorityPlayerText = ""; + if (!isGameUnderControl()) { + priorityPlayerText = " / priority " + game.getPlayer(game.getPriorityPlayerId()).getName(); + } + if (!chooseUse(Outcome.Detriment, GameLog.getPlayerConfirmColoredText("You have still mana in your mana pool. Pass regardless?") + + GameLog.getSmallSecondLineText(activePlayerText + " / " + game.getStep().getType().toString() + priorityPlayerText), game)) { + sendPlayerAction(PlayerAction.PASS_PRIORITY_CANCEL_ALL_ACTIONS, game); + return false; + } + } + pass(game); + return true; + } } diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 2a4cee3daee..73701d39fa4 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -142,7 +142,7 @@ public class Session { if (user == null) { user = UserManager.getInstance().findUser("Admin"); } - user.setUserData(new UserData(UserGroup.ADMIN, 0, false, false, null)); + user.setUserData(new UserData(UserGroup.ADMIN, 0, false, false, false, null)); if (!UserManager.getInstance().connectToSession(sessionId, user.getId())) { logger.info("Error connecting Admin!"); } @@ -154,7 +154,9 @@ public class Session { if (user != null) { UserData userData = user.getUserData(); if (userData == null) { - userData = new UserData(UserGroup.PLAYER, userDataView.getAvatarId(), userDataView.isShowAbilityPickerForced(), userDataView.allowRequestShowHandCards(), userDataView.getUserSkipPrioritySteps()); + userData = new UserData(UserGroup.PLAYER, userDataView.getAvatarId(), + userDataView.isShowAbilityPickerForced(), userDataView.allowRequestShowHandCards(), + userDataView.confirmEmptyManaPool(), userDataView.getUserSkipPrioritySteps()); user.setUserData(userData); } else { if (userDataView.getAvatarId() == 51) { // Update special avatar if first avatar is selected @@ -164,6 +166,7 @@ public class Session { userData.setShowAbilityPickerForced(userDataView.isShowAbilityPickerForced()); userData.setAllowRequestShowHandCards(userDataView.allowRequestShowHandCards()); userData.setUserSkipPrioritySteps(userDataView.getUserSkipPrioritySteps()); + userData.setConfirmEmptyManaPool(userDataView.confirmEmptyManaPool()); } return true; } diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 1d3bd46276f..e0864e3627b 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -573,6 +573,10 @@ public abstract class PlayerImpl implements Player, Serializable { playersUnderYourControl.clear(); } + /** + * returns true if the player has the control itself - false if the player is controlled by another player + * @return + */ @Override public boolean isGameUnderControl() { return isGameUnderControl; @@ -3132,5 +3136,4 @@ public abstract class PlayerImpl implements Player, Serializable { public void abortReset() { abort = false; } - } diff --git a/Mage/src/mage/players/net/UserData.java b/Mage/src/mage/players/net/UserData.java index fe2a2ce1649..71090e3e18c 100644 --- a/Mage/src/mage/players/net/UserData.java +++ b/Mage/src/mage/players/net/UserData.java @@ -13,14 +13,17 @@ public class UserData implements Serializable { protected int avatarId; protected boolean showAbilityPickerForced; protected boolean allowRequestShowHandCards; + protected boolean confirmEmptyManaPool; protected UserSkipPrioritySteps userSkipPrioritySteps; - public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, UserSkipPrioritySteps userSkipPrioritySteps) { + public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced, + boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps) { this.groupId = userGroup.getGroupId(); this.avatarId = avatarId; this.showAbilityPickerForced = showAbilityPickerForced; this.allowRequestShowHandCards = allowRequestShowHandCards; this.userSkipPrioritySteps = userSkipPrioritySteps; + this.confirmEmptyManaPool = confirmEmptyManaPool; } public void setGroupId(int groupId) { @@ -66,5 +69,13 @@ public class UserData implements Serializable { public void setUserSkipPrioritySteps(UserSkipPrioritySteps userSkipPrioritySteps) { this.userSkipPrioritySteps = userSkipPrioritySteps; } + + public boolean confirmEmptyManaPool() { + return confirmEmptyManaPool; + } + + public void setConfirmEmptyManaPool(boolean confirmEmptyManaPool) { + this.confirmEmptyManaPool = confirmEmptyManaPool; + } } diff --git a/Mage/src/mage/util/GameLog.java b/Mage/src/mage/util/GameLog.java index c71081c96dc..b302a09d3dc 100644 --- a/Mage/src/mage/util/GameLog.java +++ b/Mage/src/mage/util/GameLog.java @@ -39,6 +39,7 @@ public class GameLog { static final String LOG_COLOR_PLAYER = "#20B2AA"; // LightSeaGreen static final String LOG_COLOR_PLAYER_REQUEST = "#D2691E"; // Chocolate + static final String LOG_COLOR_PLAYER_CONFIRM = "#D2691E"; // Chocolate static final String LOG_COLOR_GREEN = "#90EE90"; // LightGreen static final String LOG_COLOR_RED = "#FF6347"; // Tomato static final String LOG_COLOR_BLUE = "#87CEFA"; // LightSkyBlue @@ -70,6 +71,14 @@ public class GameLog { return "" + name + ""; } + public static String getPlayerConfirmColoredText(String name) { + return "" + name + ""; + } + + public static String getSmallSecondLineText(String text) { + return "
" + text + "
"; + } + private static String getColorName(ObjectColor objectColor) { if (objectColor.isMulticolored()) { return LOG_COLOR_MULTI; From 3b61a10237caa03bbfeaf6659e5bd0f6724e0425 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 8 Jun 2015 18:31:54 +0200 Subject: [PATCH 077/102] Fixed some bugs and added a AI Test player class. --- .../sample-decks/Commander/Tasigur BGU.dck | 97 + .../src/mage/player/ai/ComputerPlayer7.java | 1 + .../java/mage/player/ai/ComputerPlayer.java | 2 +- .../src/mage/player/human/HumanPlayer.java | 6 + .../sets/avacynrestored/DevoutChaplain.java | 2 +- .../src/mage/sets/commander/SewerNemesis.java | 4 +- .../newphyrexia/SheoldredWhisperingOne.java | 6 + .../src/mage/sets/tempest/DiabolicEdict.java | 1 + .../mage/test/AI/basic/CastCreaturesTest.java | 54 + .../cards/abilities/keywords/FadingTest.java | 2 +- .../cards/dynamicvalue/SewerNemesisTest.java | 6 +- .../replacement/SigardaHostOfHeronsTest.java | 17 +- .../org/mage/test/player/RandomPlayer.java | 31 +- .../java/org/mage/test/player/TestPlayer.java | 1712 +++++++++++++---- .../test/serverside/base/CardTestAPI.java | 8 +- .../serverside/base/CardTestPlayerBaseAI.java | 69 + .../test/serverside/base/MageTestBase.java | 1 - .../serverside/base/MageTestPlayerBase.java | 21 +- .../serverside/base/impl/CardTestAPIImpl.java | 40 +- .../base/impl/CardTestPlayerAPIImpl.java | 15 +- .../effects/common/SacrificeEffect.java | 5 +- Mage/src/mage/game/GameImpl.java | 2 +- Mage/src/mage/game/GameState.java | 7 +- Mage/src/mage/players/Player.java | 3 + Mage/src/mage/players/PlayerImpl.java | 8 +- 25 files changed, 1732 insertions(+), 388 deletions(-) create mode 100644 Mage.Client/release/sample-decks/Commander/Tasigur BGU.dck create mode 100644 Mage.Tests/src/test/java/org/mage/test/AI/basic/CastCreaturesTest.java create mode 100644 Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java diff --git a/Mage.Client/release/sample-decks/Commander/Tasigur BGU.dck b/Mage.Client/release/sample-decks/Commander/Tasigur BGU.dck new file mode 100644 index 00000000000..5c185ee1268 --- /dev/null +++ b/Mage.Client/release/sample-decks/Commander/Tasigur BGU.dck @@ -0,0 +1,97 @@ +1 [MMQ:316] Dust Bowl +1 [MBS:43] Go for the Throat +1 [ALL:42] Force of Will +1 [ZEN:220] Misty Rainforest +1 [10E:361] Treetop Village +1 [10E:362] Underground River +1 [ROE:115] Inquisition of Kozilek +1 [STH:137] Volrath's Stronghold +1 [PLC:85] Damnation +1 [FUT:173] Tolaria West +1 [ISD:78] Snapcaster Mage +1 [ZEN:67] Spell Pierce +1 [ZEN:229] Verdant Catacombs +1 [M11:70] Preordain +1 [C13:177] Baleful Strix +1 [ZEN:223] Scalding Tarn +1 [MBS:138] Sword of Feast and Famine +1 [NPH:57] Dismember +1 [TMP:58] Dismiss +1 [INV:57] Fact or Fiction +1 [DGM:93] Putrefy +1 [ONS:320] Lonely Sandbar +1 [GTC:240] Breeding Pool +1 [DTK:262] Forest +1 [ISD:105] Liliana of the Veil +1 [TMP:340] Wasteland +1 [JUD:46] Mental Note +1 [ULG:36] Miscalculation +2 [CSP:152] Snow-Covered Island +1 [RAV:63] Remand +1 [LRW:56] Cryptic Command +1 [3ED:283] Bayou +1 [5ED:191] Sylvan Library +1 [CMD:269] Command Tower +1 [EXO:35] Forbid +1 [DDO:36] AEtherize +1 [KTK:36] Dig Through Time +1 [RTR:141] Abrupt Decay +1 [SHM:280] Sunken Ruins +1 [DIS:33] Spell Snare +1 [ARB:92] Maelstrom Pulse +1 [KTK:81] Murderous Cut +1 [TSP:48] Ancestral Vision +1 [TMP:22] Diabolic Edict +1 [10E:319] Crucible of Worlds +1 [M12:63] Mana Leak +1 [FUT:52] Logic Knot +1 [CHK:268] Sensei's Divining Top +1 [THS:225] Temple of Deceit +1 [ODY:317] Cephalid Coliseum +1 [WWK:145] Tectonic Edge +1 [EVE:41] Raven's Crime +1 [C13:96] Toxic Deluge +1 [9ED:152] Phyrexian Arena +1 [KTK:248] Windswept Heath +1 [KTK:204] Sultai Charm +1 [KTK:249] Wooded Foothills +1 [M13:223] Drowned Catacomb +1 [RAV:172] Life from the Loam +1 [THS:107] Thoughtseize +1 [MMQ:61] Brainstorm +1 [M15:244] Llanowar Wastes +1 [ISD:55] Forbidden Alchemy +1 [APC:114] Pernicious Deed +1 [RTR:243] Overgrown Tomb +1 [7ED:76] Force Spike +1 [TMP:70] Intuition +1 [3ED:305] Underground Sea +1 [M15:248] Urborg, Tomb of Yawgmoth +1 [AVR:226] Cavern of Souls +1 [ISD:241] Hinterland Harbor +1 [DKA:52] Thought Scour +1 [3ED:303] Tropical Island +1 [3ED:13] Demonic Tutor +1 [ISD:249] Woodland Cemetery +1 [M12:73] Ponder +1 [DTK:65] Negate +1 [RAV:82] Darkblast +1 [WWK:134] Creeping Tar Pit +1 [GTC:249] Watery Grave +1 [WWK:132] Bojuka Bog +1 [KTK:239] Polluted Delta +1 [KTK:233] Flooded Strand +1 [KTK:59] Treasure Cruise +1 [TSP:202] Krosan Grip +1 [7ED:67] Counterspell +1 [MIR:80] Mystical Tutor +1 [MOR:55] Vendilion Clique +1 [TSP:69] Mystical Teachings +1 [KTK:230] Bloodstained Mire +1 [ZEN:219] Marsh Flats +1 [DTK:256] Swamp +1 [THS:90] Hero's Downfall +3 [DTK:253] Island +1 [WWK:31] Jace, the Mind Sculptor +1 [DTK:98] Duress +SB: 1 [FRF:87] Tasigur, the Golden Fang diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java index 6c28193b776..22037b2ad53 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer7.java @@ -84,6 +84,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 { public ComputerPlayer7(final ComputerPlayer7 player) { super(player); + this.allowBadMoves = player.allowBadMoves; } @Override diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 8b8d0febd63..6e7e2e41228 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -1595,7 +1595,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } @Override - protected List getAvailableManaProducers(Game game) { + public List getAvailableManaProducers(Game game) { return super.getAvailableManaProducers(game); } diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 26308d76169..77bb838f1de 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -1038,6 +1038,12 @@ public class HumanPlayer extends PlayerImpl { } } + @Override + public boolean activateAbility(ActivatedAbility ability, Game game) { + getManaPool().setStock(); // needed for the "mana already in the pool has to be used manually" option + return super.activateAbility(ability, game); + } + protected void activateAbility(LinkedHashMap abilities, MageObject object, Game game) { updateGameStatePriority("activateAbility", game); if (abilities.size() == 1 && suppressAbilityPicker(abilities.values().iterator().next())) { diff --git a/Mage.Sets/src/mage/sets/avacynrestored/DevoutChaplain.java b/Mage.Sets/src/mage/sets/avacynrestored/DevoutChaplain.java index a1a951f8103..3b79a31ed8e 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/DevoutChaplain.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/DevoutChaplain.java @@ -78,7 +78,7 @@ public class DevoutChaplain extends CardImpl { this.power = new MageInt(2); this.toughness = new MageInt(2); - // {tap}, Tap two untapped Humans you control: Exile target artifact or enchantment. + // {T}, Tap two untapped Humans you control: Exile target artifact or enchantment. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(), new TapSourceCost()); ability.addCost(new TapTargetCost(new TargetControlledPermanent(2, 2, humanFilter, false))); ability.addTarget(new TargetPermanent(filter)); diff --git a/Mage.Sets/src/mage/sets/commander/SewerNemesis.java b/Mage.Sets/src/mage/sets/commander/SewerNemesis.java index 5942953b0c1..ee6da4ab795 100644 --- a/Mage.Sets/src/mage/sets/commander/SewerNemesis.java +++ b/Mage.Sets/src/mage/sets/commander/SewerNemesis.java @@ -102,11 +102,11 @@ class SewerNemesisChoosePlayerEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); Permanent permanent = game.getPermanent(source.getSourceId()); if (player != null && permanent != null) { - TargetPlayer target = new TargetPlayer(); + TargetPlayer target = new TargetPlayer(1,1,true); if (player.choose(this.outcome, target, source.getSourceId(), game)) { Player chosenPlayer = game.getPlayer(target.getFirstTarget()); if (chosenPlayer != null) { - game.informPlayers(permanent.getName() + ": " + player.getLogName() + " has chosen " + chosenPlayer.getLogName()); + game.informPlayers(permanent.getLogName() + ": " + player.getLogName() + " has chosen " + chosenPlayer.getLogName()); game.getState().setValue(permanent.getId() + "_player", target.getFirstTarget()); return true; } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/SheoldredWhisperingOne.java b/Mage.Sets/src/mage/sets/newphyrexia/SheoldredWhisperingOne.java index cc7e64b8930..fa84cf9d8d2 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/SheoldredWhisperingOne.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/SheoldredWhisperingOne.java @@ -58,10 +58,16 @@ public class SheoldredWhisperingOne extends CardImpl { this.power = new MageInt(6); this.toughness = new MageInt(6); + + // Swampwalk this.addAbility(new SwampwalkAbility()); + + // At the beginning of your upkeep, return target creature card from your graveyard to the battlefield. Ability ability = new BeginningOfUpkeepTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(false), TargetController.YOU, false); ability.addTarget(new TargetCardInYourGraveyard(new FilterCreatureCard("creature card from your graveyard"))); this.addAbility(ability); + + // At the beginning of each opponent's upkeep, that player sacrifices a creature. ability = new BeginningOfUpkeepTriggeredAbility(new SacrificeEffect(new FilterCreaturePermanent(), 1, "that player "), TargetController.OPPONENT, false); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/tempest/DiabolicEdict.java b/Mage.Sets/src/mage/sets/tempest/DiabolicEdict.java index 638f4e0b470..a6ad0c8a3c7 100644 --- a/Mage.Sets/src/mage/sets/tempest/DiabolicEdict.java +++ b/Mage.Sets/src/mage/sets/tempest/DiabolicEdict.java @@ -45,6 +45,7 @@ public class DiabolicEdict extends CardImpl { super(ownerId, 22, "Diabolic Edict", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{B}"); this.expansionSetCode = "TMP"; + // Target player sacrifices a creature. this.getSpellAbility().addEffect(new SacrificeEffect(new FilterCreaturePermanent(), 1, "Target player")); this.getSpellAbility().addTarget(new TargetPlayer()); } diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastCreaturesTest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastCreaturesTest.java new file mode 100644 index 00000000000..f7b3649f421 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastCreaturesTest.java @@ -0,0 +1,54 @@ +/* + * 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.AI.basic; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBaseAI; + +/** + * + * @author LevelX2 + */ +public class CastCreaturesTest extends CardTestPlayerBaseAI { + + /** + * Tests that the creature is cast if enough mana is available + */ + @Test + public void testSimpleCast() { + addCard(Zone.HAND, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Silvercoat Lion", 1); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FadingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FadingTest.java index 77cee8a7741..078942e082b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FadingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/FadingTest.java @@ -91,7 +91,7 @@ public class FadingTest extends CardTestPlayerBase { public void testFadesAway() { addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); addCard(Zone.HAND, playerA, "Blastoderm"); - + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blastoderm"); setStopAt(9, PhaseStep.BEGIN_COMBAT); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/SewerNemesisTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/SewerNemesisTest.java index 37abc2148ea..6ca2b46617a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/SewerNemesisTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/SewerNemesisTest.java @@ -41,12 +41,16 @@ public class SewerNemesisTest extends CardTestPlayerBase { /** - * Sewer Nemesis count's all cards in each player's graveyard to determine it's * / *, not just the cosen player's graveyard. + * BUG: Sewer Nemesis count's all cards in each player's graveyard to determine it's * / *, not just the chosen player's graveyard. * */ @Test public void test1() { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4); + + // As Sewer Nemesis enters the battlefield, choose a player. + // Sewer Nemesis's power and toughness are each equal to the number of cards in the chosen player's graveyard. + // Whenever the chosen player casts a spell, that player puts the top card of his or her library into his or her graveyard. addCard(Zone.HAND, playerA, "Sewer Nemesis"); addCard(Zone.GRAVEYARD, playerA, "Raging Goblin",4); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/SigardaHostOfHeronsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/SigardaHostOfHeronsTest.java index e02c0c7ed97..3148f858b2e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/SigardaHostOfHeronsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/SigardaHostOfHeronsTest.java @@ -18,18 +18,25 @@ public class SigardaHostOfHeronsTest extends CardTestPlayerBase { */ @Test public void testCard() { + // Spells and abilities your opponents control can't cause you to sacrifice permanents. addCard(Zone.BATTLEFIELD, playerA, "Sigarda, Host of Herons"); + // {T}, Tap two untapped Humans you control: Exile target artifact or enchantment. addCard(Zone.BATTLEFIELD, playerA, "Devout Chaplain"); + // {2}{B}, Sacrifice a creature: Target opponent reveals his or her hand. You choose a card from it. That player discards that card. Activate this ability only any time you could cast a sorcery. addCard(Zone.BATTLEFIELD, playerA, "Corpse Traders"); + // Target player sacrifices a creature. addCard(Zone.HAND, playerA, "Diabolic Edict"); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); - + addCard(Zone.HAND, playerB, "Diabolic Edict"); addCard(Zone.BATTLEFIELD, playerB, "Swamp", 2); + + // At the beginning of your upkeep, return target creature card from your graveyard to the battlefield. + // At the beginning of each opponent's upkeep, that player sacrifices a creature. addCard(Zone.BATTLEFIELD, playerB, "Sheoldred, Whispering One"); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Diabolic Edict", playerA); - castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Diabolic Edict", playerB); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Diabolic Edict", playerA); // sacrificing for player A prevented by Sigarda + castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Diabolic Edict", playerB); // playerB has to sacrifice Sheldred setStopAt(3, PhaseStep.END_TURN); execute(); @@ -40,9 +47,13 @@ public class SigardaHostOfHeronsTest extends CardTestPlayerBase { assertPermanentCount(playerA, "Sigarda, Host of Herons", 1); assertPermanentCount(playerA, "Devout Chaplain", 1); assertPermanentCount(playerA, "Corpse Traders", 1); + assertGraveyardCount(playerA, "Diabolic Edict", 1); assertGraveyardCount(playerA, 1); assertPermanentCount(playerB, "Sheoldred, Whispering One", 0); + assertHandCount(playerB, "Diabolic Edict", 0); + assertGraveyardCount(playerB, "Sheoldred, Whispering One", 1); + assertGraveyardCount(playerB, "Diabolic Edict", 1); assertGraveyardCount(playerB, 2); } diff --git a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java index 435c57719db..148c3c34979 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/RandomPlayer.java @@ -102,16 +102,18 @@ public class RandomPlayer extends ComputerPlayer { List playables = getPlayableAbilities(game); Ability ability; while (true) { - if (playables.size() == 1) + if (playables.size() == 1) { ability = playables.get(0); - else + } else { ability = playables.get(rnd.nextInt(playables.size())); + } List options = getPlayableOptions(ability, game); if (!options.isEmpty()) { - if (options.size() == 1) + if (options.size() == 1) { ability = options.get(0); - else + } else { ability = options.get(rnd.nextInt(options.size())); + } } if (ability.getManaCosts().getVariableCosts().size() > 0) { int amount = getAvailableManaProducers(game).size() - ability.getManaCosts().convertedManaCost(); @@ -154,10 +156,11 @@ public class RandomPlayer extends ComputerPlayer { ability = source; } else { - if (options.size() == 1) + if (options.size() == 1) { ability = options.get(0); - else + } else { ability = options.get(rnd.nextInt(options.size())); + } } if (ability.isUsesStack()) { game.getStack().push(new StackAbility(ability, playerId)); @@ -203,15 +206,18 @@ public class RandomPlayer extends ComputerPlayer { @Override public void selectBlockers(Game game, UUID defendingPlayerId) { int numGroups = game.getCombat().getGroups().size(); - if (numGroups == 0) return; + if (numGroups == 0) { + return; + } List blockers = getAvailableBlockers(game); for (Permanent blocker: blockers) { int check = rnd.nextInt(numGroups + 1); if (check < numGroups) { CombatGroup group = game.getCombat().getGroups().get(check); - if (group.getAttackers().size() > 0) + if (group.getAttackers().size() > 0) { this.declareBlocker(this.getId(), blocker.getId(), group.getAttackers().get(0), game); + } } } actionCount++; @@ -243,8 +249,9 @@ public class RandomPlayer extends ComputerPlayer { protected boolean chooseRandomTarget(Target target, Ability source, Game game) { Set possibleTargets = target.possibleTargets(source==null?null:source.getSourceId(), playerId, game); - if (possibleTargets.isEmpty()) + if (possibleTargets.isEmpty()) { return false; + } if (!target.isRequired(source)) { if (rnd.nextInt(possibleTargets.size() + 1) == 0) { return false; @@ -300,8 +307,9 @@ public class RandomPlayer extends ComputerPlayer { @Override public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { - if (cards.isEmpty()) + if (cards.isEmpty()) { return !target.isRequired(source); + } Card card = cards.getRandom(game); target.addTarget(card.getId(), source, game); return true; @@ -373,8 +381,9 @@ public class RandomPlayer extends ComputerPlayer { public Mode chooseMode(Modes modes, Ability source, Game game) { Iterator it = modes.values().iterator(); Mode mode = it.next(); - if (modes.size() == 1) + if (modes.size() == 1) { return mode; + } int modeNum = rnd.nextInt(modes.values().size()); for (int i = 0; i < modeNum; i++) { mode = it.next(); diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 60bc095e147..4c7c0e855e2 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -1,42 +1,65 @@ /* -* 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. -*/ - + * 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.player; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; import mage.MageObject; -import mage.abilities.*; +import mage.abilities.Abilities; +import mage.abilities.Ability; +import mage.abilities.ActivatedAbility; +import mage.abilities.Mode; +import mage.abilities.Modes; +import mage.abilities.SpellAbility; +import mage.abilities.TriggeredAbility; +import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.VariableCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.mana.ManaAbility; +import mage.abilities.mana.ManaOptions; +import mage.cards.Card; +import mage.cards.Cards; +import mage.cards.decks.Deck; import mage.choices.Choice; +import mage.constants.ManaType; import mage.constants.Outcome; import mage.constants.PhaseStep; import mage.constants.RangeOfInfluence; import mage.constants.SpellAbilityType; +import mage.constants.Zone; import mage.counters.Counter; +import mage.counters.Counters; import mage.filter.FilterPermanent; import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterCreatureForCombat; @@ -47,59 +70,65 @@ import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.permanent.AttackingPredicate; import mage.filter.predicate.permanent.BlockingPredicate; import mage.game.Game; +import mage.game.Graveyard; +import mage.game.Table; +import mage.game.combat.CombatGroup; +import mage.game.draft.Draft; +import mage.game.match.Match; +import mage.game.match.MatchPlayer; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; +import mage.game.tournament.Tournament; import mage.player.ai.ComputerPlayer; +import mage.players.Library; +import mage.players.ManaPool; import mage.players.Player; +import mage.players.net.UserData; import mage.target.Target; +import mage.target.TargetAmount; +import mage.target.TargetCard; import mage.target.TargetPermanent; import mage.target.TargetPlayer; -import mage.target.common.TargetCreaturePermanentAmount; -import org.junit.Ignore; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import mage.abilities.mana.ManaAbility; -import mage.abilities.mana.ManaOptions; -import mage.cards.Card; -import mage.constants.Zone; import mage.target.TargetSource; import mage.target.TargetSpell; import mage.target.common.TargetCardInHand; +import mage.target.common.TargetCardInLibrary; +import mage.target.common.TargetCreaturePermanentAmount; import mage.target.common.TargetPermanentOrPlayer; +import org.junit.Ignore; + + /** * * @author BetaSteward_at_googlemail.com */ @Ignore -public class TestPlayer extends ComputerPlayer { +public class TestPlayer implements Player { + + private int maxCallsWithoutAction = 100; + private int foundNoAction = 0; private final List actions = new ArrayList<>(); private final List choices = new ArrayList<>(); private final List targets = new ArrayList<>(); private final List modesSet = new ArrayList<>(); - public TestPlayer(String name, RangeOfInfluence range) { - super(name, range); - human = false; + private final ComputerPlayer computerPlayer; + + + public TestPlayer(ComputerPlayer computerPlayer) { + this.computerPlayer = computerPlayer; } - public TestPlayer(final TestPlayer player) { - super(player); - } + public TestPlayer(final TestPlayer testPlayer) { + this.actions.addAll(testPlayer.actions); + this.choices.addAll(testPlayer.choices); + this.targets.addAll(testPlayer.targets); + this.modesSet.addAll(testPlayer.modesSet); + this.computerPlayer = testPlayer.computerPlayer.copy(); + this.foundNoAction = testPlayer.foundNoAction; - public void addAction(int turnNum, PhaseStep step, String action) { - actions.add(new PlayerAction(turnNum, step, action)); - } - - @Override - public int getActionCount() { - return actions.size(); } public void addChoice(String choice) { @@ -114,6 +143,205 @@ public class TestPlayer extends ComputerPlayer { targets.add(target); } + public ManaOptions getAvailableManaTest(Game game) { + return computerPlayer.getManaAvailable(game); + } + + public void addAction(int turnNum, PhaseStep step, String action) { + actions.add(new PlayerAction(turnNum, step, action)); + } + + /** + * + * @param maxCallsWithoutAction max number of priority passes a player may have for this test (default = 100) + */ + public void setMaxCallsWithoutAction(int maxCallsWithoutAction) { + this.maxCallsWithoutAction = maxCallsWithoutAction; + } + + protected Permanent findPermanent(FilterPermanent filter, UUID controllerId, Game game) { + List permanents = game.getBattlefield().getAllActivePermanents(filter, controllerId, game); + if (permanents.size() > 0) { + return permanents.get(0); + } + return null; + } + + private boolean checkExecuteCondition(String[] groups, Game game) { + if (groups[2].startsWith("spellOnStack=")) { + String spellOnStack = groups[2].substring(13); + for (StackObject stackObject : game.getStack()) { + if (stackObject.getStackAbility().toString().contains(spellOnStack)) { + return true; + } + } + return false; + } else if (groups[2].startsWith("!spellOnStack=")) { + String spellNotOnStack = groups[2].substring(14); + for (StackObject stackObject : game.getStack()) { + if (stackObject.getStackAbility().toString().contains(spellNotOnStack)) { + return false; + } + } + return true; + } else if (groups[2].startsWith("spellOnTopOfStack=")) { + String spellOnTopOFStack = groups[2].substring(18); + if (game.getStack().size() > 0) { + StackObject stackObject = game.getStack().getFirst(); + if (stackObject != null && stackObject.getStackAbility().toString().contains(spellOnTopOFStack)) { + return true; + } + } + return false; + } else if (groups[2].startsWith("manaInPool=")) { + String manaInPool = groups[2].substring(11); + int amountOfMana = Integer.parseInt(manaInPool); + return computerPlayer.getManaPool().getMana().count() >= amountOfMana; + } + return true; + } + +// private boolean checkSpellOnTopOfStackCondition(String[] groups, Game game) { +// if (groups.length > 2 && groups[2].startsWith("spellOnTopOfStack=")) { +// String spellOnTopOFStack = groups[2].substring(18); +// if (game.getStack().size() > 0) { +// StackObject stackObject = game.getStack().getFirst(); +// if (stackObject != null && stackObject.getStackAbility().toString().contains(spellOnTopOFStack)) { +// return true; +// } +// } +// return false; +// } +// return true; +// } + private boolean addTargets(Ability ability, String[] groups, Game game) { + boolean result = true; + for (int i = 1; i < groups.length; i++) { + String group = groups[i]; + if (group.startsWith("spellOnStack") || group.startsWith("spellOnTopOfStack") || group.startsWith("!spellOnStack") || group.startsWith("target=null") || group.startsWith("manaInPool=")) { + break; + } + if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) { + if (group.contains("FuseLeft-")) { + result = handleTargetString(group.substring(group.indexOf("FuseLeft-") + 9), ability, game); + } else if (group.startsWith("FuseRight-")) { + result = handleTargetString(group.substring(group.indexOf("FuseRight-") + 10), ability, game); + } else { + result = false; + } + } else { + result = handleTargetString(group, ability, game); + } + } + return result; + } + + private boolean handleTargetString(String target, Ability ability, Game game) { + boolean result = false; + if (target.startsWith("targetPlayer=")) { + result = handlePlayerTarget(target.substring(target.indexOf("targetPlayer=") + 13), ability, game); + } else if (target.startsWith("target=")) { + result = handleNonPlayerTargetTarget(target.substring(target.indexOf("target=") + 7), ability, game); + } + return result; + } + + private boolean handlePlayerTarget(String target, Ability ability, Game game) { + boolean result = true; + int targetsSet = 0; + for (Player player : game.getPlayers().values()) { + if (player.getName().equals(target)) { + ability.getTargets().get(0).addTarget(player.getId(), ability, game); + targetsSet++; + break; + } + } + if (targetsSet < 1) { + result = false; + } + return result; + } + + private boolean handleNonPlayerTargetTarget(String target, Ability ability, Game game) { + boolean result = true; + if (target == null) { + return true; // needed if spell has no target but waits until spell is on the stack + } + String[] targetList = target.split("\\^"); + int index = 0; + int targetsSet = 0; + for (String targetName : targetList) { + if (targetName.startsWith("mode=")) { + int modeNr = Integer.parseInt(targetName.substring(5, 6)); + if (modeNr == 0 || modeNr > ability.getModes().size()) { + throw new UnsupportedOperationException("Given mode number (" + modeNr + ") not available for " + ability.toString()); + } + int modeCounter = 1; + for (Mode mode : ability.getModes().values()) { + if (modeCounter == modeNr) { + ability.getModes().setMode(mode); + index = 0; // reset target index if mode changes + break; + } + modeCounter++; + } + targetName = targetName.substring(6); + } + if (ability.getTargets().size() == 0) { + throw new AssertionError("Ability has no targets. " + ability.toString()); + } + if (index >= ability.getTargets().size()) { + break; // this can happen if targets should be set but can't be used because of hexproof e.g. + } + Target currentTarget = ability.getTargets().get(index); + if (targetName.startsWith("targetPlayer=")) { + target = targetName.substring(targetName.indexOf("targetPlayer=") + 13); + for (Player player : game.getPlayers().values()) { + if (player.getName().equals(target)) { + currentTarget.addTarget(player.getId(), ability, game); + index++; + targetsSet++; + break; + } + } + } else { + for (UUID id : currentTarget.possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) { + MageObject object = game.getObject(id); + if (object != null + && ((!targetName.isEmpty() && object.getName().startsWith(targetName)) || (targetName.isEmpty() && object.getName().isEmpty()))) { + if (currentTarget.getNumberOfTargets() == 1) { + currentTarget.clearChosen(); + } + if (currentTarget instanceof TargetCreaturePermanentAmount) { + // supports only to set the complete amount to one target + TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) currentTarget; + targetAmount.setAmount(ability, game); + int amount = targetAmount.getAmountRemaining(); + targetAmount.addTarget(id, amount, ability, game); + targetsSet++; + } else { + currentTarget.addTarget(id, ability, game); + targetsSet++; + } + if (currentTarget.getTargets().size() == currentTarget.getMaxNumberOfTargets()) { + index++; + } + break; + } + } + } + } + if (targetsSet != targetList.length) { + result = false; + } + return result; + } + + @Override + public int getActionCount() { + return actions.size(); + } + @Override public TestPlayer copy() { return new TestPlayer(this); @@ -121,7 +349,8 @@ public class TestPlayer extends ComputerPlayer { @Override public boolean priority(Game game) { - for (PlayerAction action: actions) { + int numberOfActions = actions.size(); + for (PlayerAction action : actions) { if (action.getTurnNum() == game.getTurnNum() && action.getStep() == game.getStep().getType()) { if (action.getAction().startsWith("activate:")) { @@ -131,51 +360,47 @@ public class TestPlayer extends ComputerPlayer { if (groups.length > 2 && !checkExecuteCondition(groups, game)) { break; } - for (Ability ability: this.getPlayable(game, true)) { + for (Ability ability : computerPlayer.getPlayable(game, true)) { if (ability.toString().startsWith(groups[0])) { Ability newAbility = ability.copy(); if (groups.length > 1 && !groups[1].equals("target=NO_TARGET")) { - if (!addTargets(newAbility, groups, game)) { + if (!addTargets(newAbility, groups, game)) { // targets could not be set -> try next priority break; } } - this.activateAbility((ActivatedAbility)newAbility, game); + computerPlayer.activateAbility((ActivatedAbility) newAbility, game); actions.remove(action); return true; } - } - } else - - if (action.getAction().startsWith("manaActivate:")) { + } + } else if (action.getAction().startsWith("manaActivate:")) { String command = action.getAction(); command = command.substring(command.indexOf("manaActivate:") + 13); String[] groups = command.split("\\$"); - List manaPerms = this.getAvailableManaProducers(game); - for (Permanent perm: manaPerms) { - for (Ability manaAbility: perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { - if (manaAbility.toString().startsWith(groups[0])) { - Ability newManaAbility = manaAbility.copy(); - this.activateAbility((ActivatedAbility)newManaAbility, game); + List manaPerms = computerPlayer.getAvailableManaProducers(game); + for (Permanent perm : manaPerms) { + for (Ability manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + if (manaAbility.toString().startsWith(groups[0])) { + Ability newManaAbility = manaAbility.copy(); + computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); actions.remove(action); return true; - } - } + } + } } - List manaPermsWithCost = this.getAvailableManaProducersWithCost(game); - for (Permanent perm: manaPermsWithCost) { - for (ManaAbility manaAbility: perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { - if (manaAbility.toString().startsWith(groups[0]) && manaAbility.canActivate(playerId, game)) { - Ability newManaAbility = manaAbility.copy(); - this.activateAbility((ActivatedAbility)newManaAbility, game); + List manaPermsWithCost = computerPlayer.getAvailableManaProducersWithCost(game); + for (Permanent perm : manaPermsWithCost) { + for (ManaAbility manaAbility : perm.getAbilities().getAvailableManaAbilities(Zone.BATTLEFIELD, game)) { + if (manaAbility.toString().startsWith(groups[0]) && manaAbility.canActivate(computerPlayer.getId(), game)) { + Ability newManaAbility = manaAbility.copy(); + computerPlayer.activateAbility((ActivatedAbility) newManaAbility, game); actions.remove(action); return true; - } - } + } + } } - } else - - if (action.getAction().startsWith("addCounters:")) { + } else if (action.getAction().startsWith("addCounters:")) { String command = action.getAction(); command = command.substring(command.indexOf("addCounters:") + 12); String[] groups = command.split("\\$"); @@ -189,16 +414,25 @@ public class TestPlayer extends ComputerPlayer { } } } - pass(game); + computerPlayer.pass(game); + // check to prevent endless loops + if (numberOfActions == actions.size()) { + foundNoAction++; + if (foundNoAction > maxCallsWithoutAction) { + throw new AssertionError("More priority calls to " +getName() + " and doing no action than allowed (" + maxCallsWithoutAction +")"); + } + } else { + foundNoAction = 0; + } return false; } @Override public void selectAttackers(Game game, UUID attackingPlayerId) { UUID defenderId = null; - for (PlayerAction action: actions) { + for (PlayerAction action : actions) { if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("attack:")) { - + String command = action.getAction(); command = command.substring(command.indexOf("attack:") + 7); String[] groups = command.split("\\$"); @@ -206,36 +440,36 @@ public class TestPlayer extends ComputerPlayer { String group = groups[i]; if (group.startsWith("planeswalker=")) { String planeswalkerName = group.substring(group.indexOf("planeswalker=") + 13); - for (Permanent permanent :game.getBattlefield().getAllActivePermanents(new FilterPlaneswalkerPermanent(), game)) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents(new FilterPlaneswalkerPermanent(), game)) { if (permanent.getName().equals(planeswalkerName)) { - defenderId = permanent.getId(); + defenderId = permanent.getId(); } } } if (group.startsWith("defendingPlayer=")) { String defendingPlayerName = group.substring(group.indexOf("defendingPlayer=") + 16); - for (Player defendingPlayer :game.getPlayers().values()) { + for (Player defendingPlayer : game.getPlayers().values()) { if (defendingPlayer.getName().equals(defendingPlayerName)) { - defenderId = defendingPlayer.getId(); + defenderId = defendingPlayer.getId(); break; } } } } if (defenderId == null) { - for (UUID uuid: game.getCombat().getDefenders()) { + for (UUID uuid : game.getCombat().getDefenders()) { Player defender = game.getPlayer(uuid); if (defender != null) { defenderId = uuid; } } - } + } FilterCreatureForCombat filter = new FilterCreatureForCombat(); filter.add(new NamePredicate(groups[0])); filter.add(Predicates.not(new AttackingPredicate())); - Permanent attacker = findPermanent(filter, playerId, game); + Permanent attacker = findPermanent(filter, computerPlayer.getId(), game); if (attacker != null && attacker.canAttack(defenderId, game)) { - this.declareAttacker(attacker.getId(), defenderId, game, false); + computerPlayer.declareAttacker(attacker.getId(), defenderId, game, false); } } } @@ -243,8 +477,8 @@ public class TestPlayer extends ComputerPlayer { @Override public void selectBlockers(Game game, UUID defendingPlayerId) { - UUID opponentId = game.getOpponents(playerId).iterator().next(); - for (PlayerAction action: actions) { + UUID opponentId = game.getOpponents(computerPlayer.getId()).iterator().next(); + for (PlayerAction action : actions) { if (action.getTurnNum() == game.getTurnNum() && action.getAction().startsWith("block:")) { String command = action.getAction(); command = command.substring(command.indexOf("block:") + 6); @@ -252,13 +486,13 @@ public class TestPlayer extends ComputerPlayer { FilterCreatureForCombatBlock filterBlocker = new FilterCreatureForCombatBlock(); filterBlocker.add(new NamePredicate(groups[0])); filterBlocker.add(Predicates.not(new BlockingPredicate())); - Permanent blocker = findPermanent(filterBlocker, playerId, game); + Permanent blocker = findPermanent(filterBlocker, computerPlayer.getId(), game); if (blocker != null) { FilterAttackingCreature filterAttacker = new FilterAttackingCreature(); filterAttacker.add(new NamePredicate(groups[1])); Permanent attacker = findPermanent(filterAttacker, opponentId, game); if (attacker != null) { - this.declareBlocker(defendingPlayerId, blocker.getId(), attacker.getId(), game); + computerPlayer.declareBlocker(defendingPlayerId, blocker.getId(), attacker.getId(), game); } } } @@ -268,9 +502,9 @@ public class TestPlayer extends ComputerPlayer { @Override public Mode chooseMode(Modes modes, Ability source, Game game) { if (!modesSet.isEmpty() && modes.getMaxModes() > modes.getSelectedModes().size()) { - int selectedMode = Integer.parseInt(modesSet.get(0)); + int selectedMode = Integer.parseInt(modesSet.get(0)); int i = 1; - for (Mode mode: modes.values()) { + for (Mode mode : modes.values()) { if (i == selectedMode) { modesSet.remove(0); return mode; @@ -278,14 +512,14 @@ public class TestPlayer extends ComputerPlayer { i++; } } - return super.chooseMode(modes, source, game); //To change body of generated methods, choose Tools | Templates. + return computerPlayer.chooseMode(modes, source, game); //To change body of generated methods, choose Tools | Templates. } @Override public boolean choose(Outcome outcome, Choice choice, Game game) { if (!choices.isEmpty()) { - for (String choose2: choices) { - for (String choose1: choice.getChoices()) { + for (String choose2 : choices) { + for (String choose1 : choice.getChoices()) { if (choose1.equals(choose2)) { choice.setChoice(choose2); choices.remove(choose2); @@ -294,13 +528,13 @@ public class TestPlayer extends ComputerPlayer { } } } - return super.choose(outcome, choice, game); + return computerPlayer.choose(outcome, choice, game); } @Override public int chooseReplacementEffect(Map rEffects, Game game) { if (!choices.isEmpty()) { - for (String choice: choices) { + for (String choice : choices) { for (int index = 0; index < rEffects.size(); index++) { if (choice.equals(rEffects.get(index))) { choices.remove(choice); @@ -309,7 +543,7 @@ public class TestPlayer extends ComputerPlayer { } } } - return super.chooseReplacementEffect(rEffects, game); + return computerPlayer.chooseReplacementEffect(rEffects, game); } @Override @@ -322,22 +556,22 @@ public class TestPlayer extends ComputerPlayer { } else { filterPermanent = ((TargetPermanent) target).getFilter(); } - for (String choose2: choices) { + for (String choose2 : choices) { String[] targetList = choose2.split("\\^"); boolean targetFound = false; - for (String targetName: targetList) { + for (String targetName : targetList) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filterPermanent, game)) { if (target.getTargets().contains(permanent.getId())) { continue; } if (permanent.getName().equals(targetName)) { - if (target.isNotTarget() || ((TargetPermanent)target).canTarget(playerId, permanent.getId(), null, game)) { + if (target.isNotTarget() || ((TargetPermanent) target).canTarget(computerPlayer.getId(), permanent.getId(), null, game)) { target.add(permanent.getId(), game); targetFound = true; break; } - } else if ((permanent.getName()+"-"+permanent.getExpansionSetCode()).equals(targetName)) { - if (target.isNotTarget() || ((TargetPermanent)target).canTarget(playerId, permanent.getId(), null, game)) { + } else if ((permanent.getName() + "-" + permanent.getExpansionSetCode()).equals(targetName)) { + if (target.isNotTarget() || ((TargetPermanent) target).canTarget(computerPlayer.getId(), permanent.getId(), null, game)) { target.add(permanent.getId(), game); targetFound = true; break; @@ -352,13 +586,13 @@ public class TestPlayer extends ComputerPlayer { } } if (target instanceof TargetPlayer) { - for (Player player :game.getPlayers().values()) { - for (String choose2: choices) { + for (Player player : game.getPlayers().values()) { + for (String choose2 : choices) { if (player.getName().equals(choose2)) { - if (((TargetPlayer)target).canTarget(playerId, player.getId(), null, game) && !target.getTargets().contains(player.getId())) { + if (((TargetPlayer) target).canTarget(computerPlayer.getId(), player.getId(), null, game) && !target.getTargets().contains(player.getId())) { target.add(player.getId(), game); choices.remove(choose2); - return true; + return true; } } } @@ -367,14 +601,14 @@ public class TestPlayer extends ComputerPlayer { if (target instanceof TargetSource) { Set possibleTargets; TargetSource t = ((TargetSource) target); - possibleTargets = t.possibleTargets(sourceId, playerId, game); + possibleTargets = t.possibleTargets(sourceId, computerPlayer.getId(), game); for (UUID targetId : possibleTargets) { MageObject targetObject = game.getObject(targetId); if (targetObject != null) { - for (String choose2: choices) { + for (String choose2 : choices) { String[] targetList = choose2.split("\\^"); boolean targetFound = false; - for (String targetName: targetList) { + for (String targetName : targetList) { if (targetObject.getName().equals(targetName)) { List alreadyTargetted = target.getTargets(); if (t.canTarget(targetObject.getId(), game)) { @@ -389,43 +623,43 @@ public class TestPlayer extends ComputerPlayer { if (targetFound) { choices.remove(choose2); return true; - } + } } } } } } - return super.choose(outcome, target, sourceId, game, options); + return computerPlayer.choose(outcome, target, sourceId, game, options); } @Override public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) { if (!targets.isEmpty()) { - UUID abilityControllerId = playerId; + UUID abilityControllerId = computerPlayer.getId(); if (target.getTargetController() != null && target.getAbilityController() != null) { abilityControllerId = target.getAbilityController(); } if ((target instanceof TargetPermanent) || (target instanceof TargetPermanentOrPlayer)) { - for (String targetDefinition: targets) { + for (String targetDefinition : targets) { String[] targetList = targetDefinition.split("\\^"); boolean targetFound = false; - for (String targetName: targetList) { + for (String targetName : targetList) { boolean originOnly = false; boolean copyOnly = false; if (targetName.endsWith("]")) { if (targetName.endsWith("[no copy]")) { originOnly = true; - targetName = targetName.substring(0, targetName.length()-9); + targetName = targetName.substring(0, targetName.length() - 9); } if (targetName.endsWith("[only copy]")) { copyOnly = true; - targetName = targetName.substring(0, targetName.length()-11); + targetName = targetName.substring(0, targetName.length() - 11); } } - for (Permanent permanent : game.getBattlefield().getAllActivePermanents((FilterPermanent)target.getFilter(), game)) { - if (permanent.getName().equals(targetName) || (permanent.getName()+"-"+permanent.getExpansionSetCode()).equals(targetName)) { - if (((TargetPermanent)target).canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) { - if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly )) { + for (Permanent permanent : game.getBattlefield().getAllActivePermanents((FilterPermanent) target.getFilter(), game)) { + if (permanent.getName().equals(targetName) || (permanent.getName() + "-" + permanent.getExpansionSetCode()).equals(targetName)) { + if (((TargetPermanent) target).canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) { + if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) { target.add(permanent.getId(), game); targetFound = true; break; @@ -441,28 +675,28 @@ public class TestPlayer extends ComputerPlayer { } } if (target instanceof TargetPlayer) { - for (String targetDefinition: targets) { + for (String targetDefinition : targets) { if (targetDefinition.startsWith("targetPlayer=")) { String playerName = targetDefinition.substring(targetDefinition.indexOf("targetPlayer=") + 13); - for (Player player: game.getPlayers().values()) { + for (Player player : game.getPlayers().values()) { if (player.getName().equals(playerName) - && ((TargetPlayer) target).canTarget(playerId, player.getId(), source, game)) { + && ((TargetPlayer) target).canTarget(computerPlayer.getId(), player.getId(), source, game)) { target.add(player.getId(), game); return true; } } } } - + } if (target instanceof TargetCardInHand) { - for (String targetDefinition: targets) { + for (String targetDefinition : targets) { String[] targetList = targetDefinition.split("\\^"); boolean targetFound = false; - for (String targetName: targetList) { - for (Card card: this.getHand().getCards(((TargetCardInHand)target).getFilter(), game)) { - if (card.getName().equals(targetName) || (card.getName()+"-"+card.getExpansionSetCode()).equals(targetName)) { - if (((TargetCardInHand)target).canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) { + for (String targetName : targetList) { + for (Card card : computerPlayer.getHand().getCards(((TargetCardInHand) target).getFilter(), game)) { + if (card.getName().equals(targetName) || (card.getName() + "-" + card.getExpansionSetCode()).equals(targetName)) { + if (((TargetCardInHand) target).canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) { target.add(card.getId(), game); targetFound = true; break; @@ -478,41 +712,40 @@ public class TestPlayer extends ComputerPlayer { } if (target instanceof TargetSpell) { - for (String targetDefinition: targets) { + for (String targetDefinition : targets) { String[] targetList = targetDefinition.split("\\^"); boolean targetFound = false; - for (String targetName: targetList) { - for(StackObject stackObject: game.getStack()) { + for (String targetName : targetList) { + for (StackObject stackObject : game.getStack()) { if (stackObject.getName().equals(targetName)) { target.add(stackObject.getId(), game); targetFound = true; - break; - } + break; + } } - } + } if (targetFound) { targets.remove(targetDefinition); return true; - } - } + } + } } - } - return super.chooseTarget(outcome, target, source, game); + return computerPlayer.chooseTarget(outcome, target, source, game); } @Override public TriggeredAbility chooseTriggeredAbility(List abilities, Game game) { if (!choices.isEmpty()) { - for(TriggeredAbility ability :abilities) { + for (TriggeredAbility ability : abilities) { if (ability.toString().startsWith(choices.get(0))) { choices.remove(0); return ability; } } } - return super.chooseTriggeredAbility(abilities, game); + return computerPlayer.chooseTriggeredAbility(abilities, game); } @Override @@ -539,7 +772,7 @@ public class TestPlayer extends ComputerPlayer { return xValue; } } - return super.announceXMana(min, max, message, game, ability); + return computerPlayer.announceXMana(min, max, message, game, ability); } @Override @@ -551,7 +784,7 @@ public class TestPlayer extends ComputerPlayer { return xValue; } } - return super.announceXCost(min, max, message, game, ability, null); + return computerPlayer.announceXCost(min, max, message, game, ability, null); } @Override @@ -563,190 +796,1017 @@ public class TestPlayer extends ComputerPlayer { return xValue; } } - return super.getAmount(min, max, message, game); + return computerPlayer.getAmount(min, max, message, game); } - protected Permanent findPermanent(FilterPermanent filter, UUID controllerId, Game game) { - List permanents = game.getBattlefield().getAllActivePermanents(filter, controllerId, game); - if (permanents.size() > 0) { - return permanents.get(0); - } - return null; + @Override + public void addAbility(Ability ability) { + computerPlayer.addAbility(ability); } - private boolean checkExecuteCondition(String[] groups, Game game) { - if (groups[2].startsWith("spellOnStack=")) { - String spellOnStack = groups[2].substring(13); - for (StackObject stackObject: game.getStack()) { - if (stackObject.getStackAbility().toString().contains(spellOnStack)) { - return true; - } - } - return false; - } else if (groups[2].startsWith("!spellOnStack=")) { - String spellNotOnStack = groups[2].substring(14); - for (StackObject stackObject: game.getStack()) { - if (stackObject.getStackAbility().toString().contains(spellNotOnStack)) { - return false; - } - } - return true; - } else if (groups[2].startsWith("spellOnTopOfStack=")) { - String spellOnTopOFStack = groups[2].substring(18); - if (game.getStack().size() > 0) { - StackObject stackObject = game.getStack().getFirst(); - if (stackObject != null && stackObject.getStackAbility().toString().contains(spellOnTopOFStack)) { - return true; - } - } - return false; - } else if (groups[2].startsWith("manaInPool=")) { - String manaInPool = groups[2].substring(11); - int amountOfMana = Integer.parseInt(manaInPool); - return this.getManaPool().getMana().count() >= amountOfMana; - } - return true; + @Override + public boolean activateAbility(ActivatedAbility ability, Game game) { + return computerPlayer.activateAbility(ability, game); } -// private boolean checkSpellOnTopOfStackCondition(String[] groups, Game game) { -// if (groups.length > 2 && groups[2].startsWith("spellOnTopOfStack=")) { -// String spellOnTopOFStack = groups[2].substring(18); -// if (game.getStack().size() > 0) { -// StackObject stackObject = game.getStack().getFirst(); -// if (stackObject != null && stackObject.getStackAbility().toString().contains(spellOnTopOFStack)) { -// return true; -// } -// } -// return false; -// } -// return true; -// } - - private boolean addTargets(Ability ability, String[] groups, Game game) { - boolean result = true; - for (int i = 1; i < groups.length; i++) { - String group = groups[i]; - if (group.startsWith("spellOnStack") || group.startsWith("spellOnTopOfStack") || group.startsWith("!spellOnStack") || group.startsWith("target=null") || group.startsWith("manaInPool=")) { - break; - } - if (ability instanceof SpellAbility && ((SpellAbility) ability).getSpellAbilityType().equals(SpellAbilityType.SPLIT_FUSED)) { - if (group.contains("FuseLeft-")) { - result = handleTargetString(group.substring(group.indexOf("FuseLeft-") + 9), ability, game); - } else if(group.startsWith("FuseRight-")) { - result = handleTargetString(group.substring(group.indexOf("FuseRight-") + 10), ability, game); - } else { - result = false; - } - } else { - result = handleTargetString(group, ability, game); - } - } - return result; + @Override + public void abort() { + computerPlayer.abort(); } - private boolean handleTargetString(String target, Ability ability, Game game){ - boolean result = false; - if (target.startsWith("targetPlayer=")) { - result = handlePlayerTarget(target.substring(target.indexOf("targetPlayer=") + 13), ability, game); - } else if (target.startsWith("target=")) { - result = handleNonPlayerTargetTarget(target.substring(target.indexOf("target=") + 7), ability, game); - } - return result; - } - - private boolean handlePlayerTarget(String target, Ability ability, Game game) { - boolean result = true; - int targetsSet = 0; - for (Player player: game.getPlayers().values()) { - if (player.getName().equals(target)) { - ability.getTargets().get(0).addTarget(player.getId(), ability, game); - targetsSet++; - break; - } - } - if (targetsSet < 1) { - result = false; - } - return result; - } - - private boolean handleNonPlayerTargetTarget(String target, Ability ability, Game game) { - boolean result = true; - if (target == null) { - return true; // needed if spell has no target but waits until spell is on the stack - } - String[] targetList = target.split("\\^"); - int index = 0; - int targetsSet = 0; - for (String targetName: targetList) { - if (targetName.startsWith("mode=")) { - int modeNr = Integer.parseInt(targetName.substring(5, 6)); - if (modeNr == 0 || modeNr > ability.getModes().size()) { - throw new UnsupportedOperationException("Given mode number (" + modeNr + ") not available for " + ability.toString()); - } - int modeCounter = 1; - for (Mode mode :ability.getModes().values()) { - if (modeCounter == modeNr) { - ability.getModes().setMode(mode); - index=0; // reset target index if mode changes - break; - } - modeCounter++; - } - targetName = targetName.substring(6); - } - if (ability.getTargets().size() == 0) { - throw new AssertionError("Ability has no targets. " + ability.toString()); - } - if (index >= ability.getTargets().size()) { - break; // this can happen if targets should be set but can't be used because of hexproof e.g. - } - Target currentTarget = ability.getTargets().get(index); - if (targetName.startsWith("targetPlayer=")) { - target = targetName.substring(targetName.indexOf("targetPlayer=") + 13); - for (Player player: game.getPlayers().values()) { - if (player.getName().equals(target)) { - currentTarget.addTarget(player.getId(), ability, game); - index++; - targetsSet++; - break; - } - } - } else { - for (UUID id: currentTarget.possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) { - MageObject object = game.getObject(id); - if (object != null && - ((!targetName.isEmpty() && object.getName().startsWith(targetName)) || (targetName.isEmpty() && object.getName().isEmpty()))) { - if (currentTarget.getNumberOfTargets() == 1) { - currentTarget.clearChosen(); - } - if (currentTarget instanceof TargetCreaturePermanentAmount) { - // supports only to set the complete amount to one target - TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) currentTarget; - targetAmount.setAmount(ability, game); - int amount = targetAmount.getAmountRemaining(); - targetAmount.addTarget(id, amount,ability, game); - targetsSet++; - } else { - currentTarget.addTarget(id, ability, game); - targetsSet++; - } - if (currentTarget.getTargets().size() == currentTarget.getMaxNumberOfTargets()) { - index++; - } - break; - } - } - } - } - if (targetsSet != targetList.length) { - result = false; - } - return result; + @Override + public void abortReset() { + computerPlayer.abortReset(); } - public ManaOptions getAvailableManaTest(Game game) { - return getManaAvailable(game); + @Override + public void won(Game game) { + computerPlayer.won(game); } + + @Override + public void restore(Player player) { + computerPlayer.restore(player); + } + + @Override + public void useDeck(Deck deck, Game game) { + computerPlayer.useDeck(deck, game); + } + + @Override + public void init(Game game) { + computerPlayer.init(game); + } + + @Override + public void init(Game game, boolean testMode) { + computerPlayer.init(game, testMode); + } + + @Override + public void reset() { + computerPlayer.reset(); + } + + @Override + public Counters getCounters() { + return computerPlayer.getCounters(); + } + + @Override + public void otherPlayerLeftGame(Game game) { + computerPlayer.otherPlayerLeftGame(game); + } + + @Override + public void beginTurn(Game game) { + computerPlayer.beginTurn(game); + } + + @Override + public RangeOfInfluence getRange() { + return computerPlayer.getRange(); + } + + @Override + public Set getInRange() { + return computerPlayer.getInRange(); + } + + @Override + public Set getPlayersUnderYourControl() { + return computerPlayer.getPlayersUnderYourControl(); + } + + @Override + public void controlPlayersTurn(Game game, UUID playerId) { + computerPlayer.controlPlayersTurn(game, playerId); + } + + @Override + public void setTurnControlledBy(UUID playerId) { + computerPlayer.setTurnControlledBy(playerId); + } + + @Override + public UUID getTurnControlledBy() { + return computerPlayer.getTurnControlledBy(); + } + + @Override + public void resetOtherTurnsControlled() { + computerPlayer.resetOtherTurnsControlled(); + } + + @Override + public boolean isGameUnderControl() { + return computerPlayer.isGameUnderControl(); + } + + @Override + public void setGameUnderYourControl(boolean value) { + computerPlayer.setGameUnderYourControl(value); + } + + @Override + public void endOfTurn(Game game) { + computerPlayer.endOfTurn(game); + } + + @Override + public boolean canBeTargetedBy(MageObject source, UUID sourceControllerId, Game game) { + return computerPlayer.canBeTargetedBy(source, sourceControllerId, game); + } + + @Override + public boolean hasProtectionFrom(MageObject source, Game game) { + return computerPlayer.hasProtectionFrom(source, game); + } + + @Override + public int drawCards(int num, Game game) { + return computerPlayer.drawCards(num, game); + } + + @Override + public int drawCards(int num, Game game, ArrayList appliedEffects) { + return computerPlayer.drawCards(num, game, appliedEffects); + } + + @Override + public void discardToMax(Game game) { + computerPlayer.discardToMax(game); + } + + @Override + public boolean putInHand(Card card, Game game) { + return computerPlayer.putInHand(card, game); + } + + @Override + public boolean removeFromHand(Card card, Game game) { + return computerPlayer.removeFromHand(card, game); + } + + @Override + public boolean removeFromLibrary(Card card, Game game) { + return computerPlayer.removeFromLibrary(card, game); + } + + @Override + public void discard(int amount, Ability source, Game game) { + computerPlayer.discard(amount, source, game); + } + + @Override + public Card discardOne(boolean random, Ability source, Game game) { + return computerPlayer.discardOne(random, source, game); + } + + @Override + public Cards discard(int amount, boolean random, Ability source, Game game) { + return computerPlayer.discard(amount, random, source, game); + } + + @Override + public boolean discard(Card card, Ability source, Game game) { + return computerPlayer.discard(card, source, game); + } + + @Override + public List getAttachments() { + return computerPlayer.getAttachments(); + } + + @Override + public boolean addAttachment(UUID permanentId, Game game) { + return computerPlayer.addAttachment(permanentId, game); + } + + @Override + public boolean removeAttachment(Permanent attachment, Game game) { + return computerPlayer.removeAttachment(attachment, game); + } + + @Override + public boolean removeFromBattlefield(Permanent permanent, Game game) { + return computerPlayer.removeFromBattlefield(permanent, game); + } + + @Override + public boolean putInGraveyard(Card card, Game game, boolean fromBattlefield) { + return computerPlayer.putInGraveyard(card, game, fromBattlefield); + } + + @Override + public boolean removeFromGraveyard(Card card, Game game) { + return computerPlayer.removeFromGraveyard(card, game); + } + + @Override + public boolean putCardsOnBottomOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder) { + return computerPlayer.putCardsOnBottomOfLibrary(cards, game, source, anyOrder); + } + + @Override + public boolean putCardsOnTopOfLibrary(Cards cards, Game game, Ability source, boolean anyOrder) { + return computerPlayer.putCardsOnTopOfLibrary(cards, game, source, anyOrder); + } + + @Override + public void setCastSourceIdWithAlternateMana(UUID sourceId, ManaCosts manaCosts) { + computerPlayer.setCastSourceIdWithAlternateMana(sourceId, manaCosts); + } + + @Override + public UUID getCastSourceIdWithAlternateMana() { + return computerPlayer.getCastSourceIdWithAlternateMana(); + } + + @Override + public ManaCosts getCastSourceIdManaCosts() { + return computerPlayer.getCastSourceIdManaCosts(); + } + + @Override + public boolean isInPayManaMode() { + return computerPlayer.isInPayManaMode(); + } + + @Override + public boolean cast(SpellAbility ability, Game game, boolean noMana) { + return computerPlayer.cast(ability, game, noMana); + } + + @Override + public boolean playLand(Card card, Game game) { + return computerPlayer.playLand(card, game); + } + + @Override + public boolean triggerAbility(TriggeredAbility source, Game game) { + return computerPlayer.triggerAbility(source, game); + } + + @Override + public LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game) { + return computerPlayer.getUseableActivatedAbilities(object, zone, game); + } + + @Override + public int getLandsPlayed() { + return computerPlayer.getLandsPlayed(); + } + + @Override + public boolean canPlayLand() { + return computerPlayer.canPlayLand(); + } + + @Override + public void shuffleLibrary(Game game) { + computerPlayer.shuffleLibrary(game); + } + + @Override + public void revealCards(String name, Cards cards, Game game) { + computerPlayer.revealCards(name, cards, game); + } + + @Override + public void revealCards(String name, Cards cards, Game game, boolean postToLog) { + computerPlayer.revealCards(name, cards, game, postToLog); + } + + @Override + public void lookAtCards(String name, Cards cards, Game game) { + computerPlayer.lookAtCards(name, cards, game); + } + + @Override + public void phasing(Game game) { + computerPlayer.phasing(game); + } + + @Override + public void untap(Game game) { + computerPlayer.untap(game); + } + + @Override + public UUID getId() { + return computerPlayer.getId(); + } + + @Override + public Cards getHand() { + return computerPlayer.getHand(); + } + + @Override + public Graveyard getGraveyard() { + return computerPlayer.getGraveyard(); + } + + @Override + public ManaPool getManaPool() { + return computerPlayer.getManaPool(); + } + + @Override + public String getName() { + return computerPlayer.getName(); + } + + @Override + public String getLogName() { + return computerPlayer.getLogName(); + } + + @Override + public boolean isHuman() { + return computerPlayer.isHuman(); + } + + @Override + public Library getLibrary() { + return computerPlayer.getLibrary(); + } + + @Override + public Cards getSideboard() { + return computerPlayer.getSideboard(); + } + + @Override + public int getLife() { + return computerPlayer.getLife(); + } + + @Override + public void initLife(int life) { + computerPlayer.initLife(life); + } + + @Override + public void setLife(int life, Game game) { + computerPlayer.setLife(life, game); + } + + @Override + public void setLifeTotalCanChange(boolean lifeTotalCanChange) { + computerPlayer.setLifeTotalCanChange(lifeTotalCanChange); + } + + @Override + public boolean isLifeTotalCanChange() { + return computerPlayer.isLifeTotalCanChange(); + } + + @Override + public List getAlternativeSourceCosts() { + return computerPlayer.getAlternativeSourceCosts(); + } + + @Override + public boolean isCanLoseLife() { + return computerPlayer.isCanLoseLife(); + } + + @Override + public void setCanLoseLife(boolean canLoseLife) { + computerPlayer.setCanLoseLife(canLoseLife); + } + + @Override + public int loseLife(int amount, Game game) { + return computerPlayer.loseLife(amount, game); + } + + @Override + public boolean isCanGainLife() { + return computerPlayer.isCanGainLife(); + } + + @Override + public void setCanGainLife(boolean canGainLife) { + computerPlayer.setCanGainLife(canGainLife); + } + + @Override + public int gainLife(int amount, Game game) { + return computerPlayer.gainLife(amount, game); + } + + @Override + public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable) { + return computerPlayer.damage(damage, sourceId, game, combatDamage, preventable); + } + + @Override + public int damage(int damage, UUID sourceId, Game game, boolean combatDamage, boolean preventable, ArrayList appliedEffects) { + return computerPlayer.damage(damage, sourceId, game, combatDamage, preventable, appliedEffects); + } + + @Override + public void addCounters(Counter counter, Game game) { + computerPlayer.addCounters(counter, game); + } + + @Override + public Abilities getAbilities() { + return computerPlayer.getAbilities(); + } + + @Override + public int getLandsPerTurn() { + return computerPlayer.getLandsPerTurn(); + } + + @Override + public void setLandsPerTurn(int landsPerTurn) { + computerPlayer.setLandsPerTurn(landsPerTurn); + } + + @Override + public int getLoyaltyUsePerTurn() { + return computerPlayer.getLoyaltyUsePerTurn(); + } + + @Override + public void setLoyaltyUsePerTurn(int loyaltyUsePerTurn) { + computerPlayer.setLoyaltyUsePerTurn(loyaltyUsePerTurn); + } + + @Override + public int getMaxHandSize() { + return computerPlayer.getMaxHandSize(); + } + + @Override + public void setMaxHandSize(int maxHandSize) { + computerPlayer.setMaxHandSize(maxHandSize); + } + + @Override + public void setMaxAttackedBy(int maxAttackedBy) { + computerPlayer.setMaxAttackedBy(maxAttackedBy); + } + + @Override + public int getMaxAttackedBy() { + return computerPlayer.getMaxAttackedBy(); + } + + @Override + public void setResponseString(String responseString) { + computerPlayer.setResponseString(responseString); + } + + @Override + public void setResponseManaType(UUID manaTypePlayerId, ManaType responseManaType) { + computerPlayer.setResponseManaType(manaTypePlayerId, responseManaType); + } + + @Override + public void setResponseUUID(UUID responseUUID) { + computerPlayer.setResponseUUID(responseUUID); + } + + @Override + public void setResponseBoolean(Boolean responseBoolean) { + computerPlayer.setResponseBoolean(responseBoolean); + } + + @Override + public void setResponseInteger(Integer responseInteger) { + computerPlayer.setResponseInteger(responseInteger); + } + + @Override + public boolean isPassed() { + return computerPlayer.isPassed(); + } + + @Override + public void pass(Game game) { + computerPlayer.pass(game); + } + + @Override + public boolean isEmptyDraw() { + return computerPlayer.isEmptyDraw(); + } + + @Override + public void resetPassed() { + computerPlayer.resetPassed(); + } + + @Override + public void quit(Game game) { + computerPlayer.quit(game); + } + + @Override + public void timerTimeout(Game game) { + computerPlayer.timerTimeout(game); + } + + @Override + public void idleTimeout(Game game) { + computerPlayer.idleTimeout(game); + } + + @Override + public void concede(Game game) { + computerPlayer.concede(game); + } + + @Override + public void sendPlayerAction(mage.constants.PlayerAction playerAction, Game game) { + computerPlayer.sendPlayerAction(playerAction, game); + } + + @Override + public void leave() { + computerPlayer.leave(); + } + + @Override + public boolean hasLeft() { + return computerPlayer.hasLeft(); + } + + @Override + public void lost(Game game) { + computerPlayer.lost(game); + } + + @Override + public void lostForced(Game game) { + computerPlayer.lostForced(game); + } + + @Override + public boolean canLose(Game game) { + return computerPlayer.canLose(game); + } + + @Override + public boolean hasLost() { + return computerPlayer.hasLost(); + } + + @Override + public boolean isInGame() { + return computerPlayer.isInGame(); + } + + @Override + public boolean hasWon() { + return computerPlayer.hasWon(); + } + + @Override + public void declareAttacker(UUID attackerId, UUID defenderId, Game game, boolean allowUndo) { + computerPlayer.declareAttacker(attackerId, defenderId, game, allowUndo); + } + + @Override + public void declareBlocker(UUID defenderId, UUID blockerId, UUID attackerId, Game game) { + computerPlayer.declareBlocker(defenderId, blockerId, attackerId, game); + } + + @Override + public boolean searchLibrary(TargetCardInLibrary target, Game game) { + return computerPlayer.searchLibrary(target, game); + } + + @Override + public boolean searchLibrary(TargetCardInLibrary target, Game game, UUID targetPlayerId) { + return computerPlayer.searchLibrary(target, game, targetPlayerId); + } + + @Override + public boolean flipCoin(Game game) { + return computerPlayer.flipCoin(game); + } + + @Override + public boolean flipCoin(Game game, ArrayList appliedEffects) { + return computerPlayer.flipCoin(game, appliedEffects); + } + + @Override + public List getAvailableAttackers(Game game) { + return computerPlayer.getAvailableAttackers(game); + } + + @Override + public List getAvailableAttackers(UUID defenderId, Game game) { + return computerPlayer.getAvailableAttackers(defenderId, game); + } + + @Override + public List getAvailableBlockers(Game game) { + return computerPlayer.getAvailableBlockers(game); + } + + @Override + public ManaOptions getManaAvailable(Game game) { + return computerPlayer.getManaAvailable(game); + } + + public List getAvailableManaProducersWithCost(Game game) { + return computerPlayer.getAvailableManaProducersWithCost(game); + } + + @Override + public List getPlayable(Game game, boolean hidden) { + return computerPlayer.getPlayable(game, hidden); + } + + @Override + public Set getPlayableInHand(Game game) { + return computerPlayer.getPlayableInHand(game); + } + + @Override + public List getPlayableOptions(Ability ability, Game game) { + return computerPlayer.getPlayableOptions(ability, game); + } + + @Override + public boolean isTestMode() { + return computerPlayer.isTestMode(); + } + + @Override + public void setTestMode(boolean value) { + computerPlayer.setTestMode(value); + } + + @Override + public boolean isTopCardRevealed() { + return computerPlayer.isTopCardRevealed(); + } + + @Override + public void setTopCardRevealed(boolean topCardRevealed) { + computerPlayer.setTopCardRevealed(topCardRevealed); + } + + @Override + public UserData getUserData() { + return computerPlayer.getUserData(); + } + + @Override + public void setUserData(UserData userData) { + computerPlayer.setUserData(userData); + } + + @Override + public void addAction(String action) { + computerPlayer.addAction(action); + } + + @Override + public void setAllowBadMoves(boolean allowBadMoves) { + computerPlayer.setAllowBadMoves(allowBadMoves); + } + + @Override + public boolean canPayLifeCost() { + return computerPlayer.canPayLifeCost(); + } + + @Override + public void setCanPayLifeCost(boolean canPayLifeCost) { + computerPlayer.setCanPayLifeCost(canPayLifeCost); + } + + @Override + public boolean canPaySacrificeCost() { + return computerPlayer.canPaySacrificeCost(); + } + + @Override + public void setCanPaySacrificeCost(boolean canPaySacrificeCost) { + computerPlayer.setCanPaySacrificeCost(canPaySacrificeCost); + } + + @Override + public boolean canLoseByZeroOrLessLife() { + return computerPlayer.canLoseByZeroOrLessLife(); + } + + @Override + public void setLoseByZeroOrLessLife(boolean loseByZeroOrLessLife) { + computerPlayer.setLoseByZeroOrLessLife(loseByZeroOrLessLife); + } + + @Override + public boolean canPlayCardsFromGraveyard() { + return computerPlayer.canPlayCardsFromGraveyard(); + } + + @Override + public void setPlayCardsFromGraveyard(boolean playCardsFromGraveyard) { + computerPlayer.setPlayCardsFromGraveyard(playCardsFromGraveyard); + } + + @Override + public boolean autoLoseGame() { + return computerPlayer.autoLoseGame(); + } + + @Override + public void becomesActivePlayer() { + computerPlayer.becomesActivePlayer(); + } + + @Override + public int getTurns() { + return computerPlayer.getTurns(); + } + + @Override + public int getStoredBookmark() { + return computerPlayer.getStoredBookmark(); + } + + @Override + public void setStoredBookmark(int storedBookmark) { + computerPlayer.setStoredBookmark(storedBookmark); + } + + @Override + public synchronized void resetStoredBookmark(Game game) { + computerPlayer.resetStoredBookmark(game); + } + + @Override + public void revealFaceDownCard(Card card, Game game) { + computerPlayer.revealFaceDownCard(card, game); + } + + @Override + public void setPriorityTimeLeft(int timeLeft) { + computerPlayer.setPriorityTimeLeft(timeLeft); + } + + @Override + public int getPriorityTimeLeft() { + return computerPlayer.getPriorityTimeLeft(); + } + + @Override + public boolean hasQuit() { + return computerPlayer.hasQuit(); + } + + @Override + public boolean hasTimerTimeout() { + return computerPlayer.hasTimerTimeout(); + } + + @Override + public boolean hasIdleTimeout() { + return computerPlayer.hasIdleTimeout(); + } + + @Override + public void setReachedNextTurnAfterLeaving(boolean reachedNextTurnAfterLeaving) { + computerPlayer.setReachedNextTurnAfterLeaving(reachedNextTurnAfterLeaving); + } + + @Override + public boolean hasReachedNextTurnAfterLeaving() { + return computerPlayer.hasReachedNextTurnAfterLeaving(); + } + + @Override + public boolean canJoinTable(Table table) { + return computerPlayer.canJoinTable(table); + } + + @Override + public void setCommanderId(UUID commanderId) { + computerPlayer.setCommanderId(commanderId); + } + + @Override + public UUID getCommanderId() { + return computerPlayer.getCommanderId(); + } + + @Override + public boolean moveCards(Cards cards, Zone fromZone, Zone toZone, Ability source, Game game) { + return computerPlayer.moveCards(cards, fromZone, toZone, source, game); + } + + @Override + public boolean moveCards(Card card, Zone fromZone, Zone toZone, Ability source, Game game) { + return computerPlayer.moveCards(card, fromZone, toZone, source, game); + } + + @Override + public boolean moveCards(List cards, Zone fromZone, Zone toZone, Ability source, Game game) { + return computerPlayer.moveCards(cards, fromZone, toZone, source, game); + } + + @Override + public boolean moveCardToHandWithInfo(Card card, UUID sourceId, Game game, Zone fromZone) { + return computerPlayer.moveCardToHandWithInfo(card, sourceId, game, fromZone); + } + + @Override + public boolean moveCardToHandWithInfo(Card card, UUID sourceId, Game game, Zone fromZone, boolean withName) { + return computerPlayer.moveCardToHandWithInfo(card, sourceId, game, fromZone, withName); + } + + @Override + public boolean moveCardsToGraveyardWithInfo(List allCards, Ability source, Game game, Zone fromZone) { + return computerPlayer.moveCardsToGraveyardWithInfo(allCards, source, game, fromZone); + } + + @Override + public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId, Game game, Zone fromZone) { + return computerPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone); + } + + @Override + public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId, Game game, Zone fromZone, boolean toTop, boolean withName) { + return computerPlayer.moveCardToLibraryWithInfo(card, sourceId, game, fromZone, toTop, withName); + } + + @Override + public boolean moveCardToExileWithInfo(Card card, UUID exileId, String exileName, UUID sourceId, Game game, Zone fromZone, boolean withName) { + return computerPlayer.moveCardToExileWithInfo(card, exileId, exileName, sourceId, game, fromZone, withName); + } + + @Override + public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId) { + return computerPlayer.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId); + } + + @Override + public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped) { + return computerPlayer.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId, tapped); + } + + @Override + public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped, boolean facedown) { + return computerPlayer.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId, tapped, facedown); + } + + @Override + public boolean hasOpponent(UUID playerToCheckId, Game game) { + return computerPlayer.hasOpponent(playerToCheckId, game); + } + + @Override + public boolean getPassedAllTurns() { + return computerPlayer.getPassedAllTurns(); + } + + @Override + public boolean getPassedUntilNextMain() { + return computerPlayer.getPassedUntilNextMain(); + } + + @Override + public boolean getPassedUntilEndOfTurn() { + return computerPlayer.getPassedUntilEndOfTurn(); + } + + @Override + public boolean getPassedTurn() { + return computerPlayer.getPassedTurn(); + } + + @Override + public boolean getPassedUntilStackResolved() { + return computerPlayer.getPassedUntilStackResolved(); + } + + @Override + public void revokePermissionToSeeHandCards() { + computerPlayer.revokePermissionToSeeHandCards(); + } + + @Override + public void addPermissionToShowHandCards(UUID watcherUserId) { + computerPlayer.addPermissionToShowHandCards(watcherUserId); + } + + @Override + public boolean isRequestToShowHandCardsAllowed() { + return computerPlayer.isRequestToShowHandCardsAllowed(); + } + + @Override + public boolean hasUserPermissionToSeeHand(UUID userId) { + return computerPlayer.hasUserPermissionToSeeHand(userId); + } + + @Override + public Set getUsersAllowedToSeeHandCards() { + return computerPlayer.getUsersAllowedToSeeHandCards(); + } + + @Override + public void setMatchPlayer(MatchPlayer matchPlayer) { + computerPlayer.setMatchPlayer(matchPlayer); + } + + @Override + public MatchPlayer getMatchPlayer() { + return computerPlayer.getMatchPlayer(); + } + + @Override + public void cleanUpOnMatchEnd() { + computerPlayer.cleanUpOnMatchEnd(); + } + + @Override + public SpellAbility chooseSpellAbilityForCast(SpellAbility ability, Game game, boolean noMana) { + return computerPlayer.chooseSpellAbilityForCast(ability, game, noMana); + } + + @Override + public void skip() { + computerPlayer.skip(); + } + + @Override + public boolean choose(Outcome outcome, Target target, UUID sourceId, Game game) { + // needed to call here the TestPlayer because it's overwitten + return choose(outcome, target, sourceId, game, null); + } + + @Override + public boolean choose(Outcome outcome, Cards cards, TargetCard target, Game game) { + return computerPlayer.choose(outcome, cards, target, game); + } + + @Override + public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) { + return computerPlayer.chooseTarget(outcome, cards, target, source, game); + } + + @Override + public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) { + return computerPlayer.chooseTargetAmount(outcome, target, source, game); + } + + @Override + public boolean chooseMulligan(Game game) { + return computerPlayer.chooseMulligan(game); + } + + @Override + public boolean choosePile(Outcome outcome, String message, List pile1, List pile2, Game game) { + return computerPlayer.choosePile(outcome, message, pile1, pile2, game); + } + + @Override + public boolean playMana(ManaCost unpaid, String promptText, Game game) { + return computerPlayer.playMana(unpaid, promptText, game); + } + + @Override + public UUID chooseAttackerOrder(List attacker, Game game) { + return computerPlayer.chooseAttackerOrder(attacker, game); + } + + @Override + public UUID chooseBlockerOrder(List blockers, CombatGroup combatGroup, List blockerOrder, Game game) { + return computerPlayer.chooseBlockerOrder(blockers, combatGroup, blockerOrder, game); + } + + @Override + public void assignDamage(int damage, List targets, String singleTargetName, UUID sourceId, Game game) { + computerPlayer.assignDamage(damage, targets, singleTargetName, sourceId, game); + } + + @Override + public void sideboard(Match match, Deck deck) { + computerPlayer.sideboard(match, deck); + } + + @Override + public void construct(Tournament tournament, Deck deck) { + computerPlayer.construct(tournament, deck); + } + + @Override + public void pickCard(List cards, Deck deck, Draft draft) { + computerPlayer.pickCard(cards, deck, draft); + } + } - diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java index 6369627bbc1..67cf6484b5f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestAPI.java @@ -1,13 +1,14 @@ package org.mage.test.serverside.base; -import mage.constants.PhaseStep; +import java.util.List; import mage.abilities.Ability; +import mage.constants.PhaseStep; import mage.constants.Zone; import mage.filter.Filter; import mage.players.Player; import org.mage.test.player.TestPlayer; -import java.util.List; + /** * Interface for all test initialization and assertion operations. @@ -81,11 +82,14 @@ public interface CardTestAPI { /** * Define turn number to stop the game on. + * @param turn */ void setStopOnTurn(int turn); /** * Define the turn number and step to stop the game on. + * @param turn + * @param step */ void setStopAt(int turn, PhaseStep step); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java new file mode 100644 index 00000000000..2fa9998510f --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java @@ -0,0 +1,69 @@ +/* + * 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.serverside.base; + +import java.io.FileNotFoundException; +import mage.constants.MultiplayerAttackOption; +import mage.constants.RangeOfInfluence; +import mage.game.Game; +import mage.game.GameException; +import mage.game.TwoPlayerDuel; +import mage.player.ai.ComputerPlayer7; +import org.mage.test.player.TestPlayer; +import org.mage.test.serverside.base.impl.CardTestPlayerAPIImpl; + +/** + * + * @author LevelX2 + */ + +public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl { + + int skill = 9; + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + Game game = new TwoPlayerDuel(MultiplayerAttackOption.LEFT, RangeOfInfluence.ONE, 0, 20); + + playerA = createPlayer(game, playerA, "PlayerA"); + playerB = createPlayer(game, playerB, "PlayerB"); + return game; + } + + @Override + protected TestPlayer createPlayer(String name) { + if (name.equals("PlayerA")) { + return new TestPlayer(new ComputerPlayer7("PlayerA", RangeOfInfluence.ONE, skill)); + } + return super.createPlayer(name); + } + + public void setAISkill(int skill) { + this.skill = skill; + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java index d1864fbf4ac..bcfa18d0f54 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java @@ -31,7 +31,6 @@ import java.io.FilenameFilter; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.mage.test.serverside.base.MageTestPlayerBase.currentGame; /** * Base class for all tests. diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java index ace998ed9b8..32805289ed6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java @@ -28,6 +28,7 @@ import java.io.FilenameFilter; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import mage.player.ai.ComputerPlayer; /** * Base class for all tests. @@ -260,14 +261,15 @@ public abstract class MageTestPlayerBase { } private TestPlayer getPlayer(String name) { - if (name.equals("ComputerA")) { - return playerA; - } else if (name.equals("ComputerB")) { - return playerB; - } else if (name.equals("ComputerC")) { - return playerC; - } else if (name.equals("ComputerD")) { - return playerD; + switch (name) { + case "ComputerA": + return playerA; + case "ComputerB": + return playerB; + case "ComputerC": + return playerC; + case "ComputerD": + return playerD; } throw new IllegalArgumentException("Couldn't find player for name=" + name); } @@ -339,6 +341,7 @@ public abstract class MageTestPlayerBase { } protected TestPlayer createPlayer(String name) { - return new TestPlayer(name, RangeOfInfluence.ONE); + return new TestPlayer(new ComputerPlayer(name, RangeOfInfluence.ONE)); } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java index 817d7fe6c2c..66423135b0f 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestAPIImpl.java @@ -28,6 +28,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP /** * Default game initialization params for red player (that plays with Mountains) */ + @Override public void useRedDefault() { // *** ComputerA *** // battlefield:ComputerA:Mountain:5 @@ -88,6 +89,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param player {@link Player} to add cards for. Use either playerA or playerB. * @param cardName Card name in string format. */ + @Override public void addCard(Zone gameZone, TestPlayer player, String cardName) { addCard(gameZone, player, cardName, 1, false); } @@ -100,6 +102,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param cardName Card name in string format. * @param count Amount of cards to be added. */ + @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) { addCard(gameZone, player, cardName, count, false); } @@ -114,6 +117,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped. * In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown */ + @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) { @@ -179,6 +183,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param player {@link Player} to set life count for. * @param life Life count to set. */ + @Override public void setLife(TestPlayer player, int life) { if (player.equals(playerA)) { commandsA.put(Zone.OUTSIDE, "life:" + String.valueOf(life)); @@ -190,16 +195,18 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP /** * Define turn number to stop the game on. */ + @Override public void setStopOnTurn(int turn) { - stopOnTurn = turn == -1 ? null : Integer.valueOf(turn); + stopOnTurn = turn == -1 ? null : turn; stopAtStep = PhaseStep.UNTAP; } /** * Define turn number and step to stop the game on. */ + @Override public void setStopAt(int turn, PhaseStep step) { - stopOnTurn = turn == -1 ? null : Integer.valueOf(turn); + stopOnTurn = turn == -1 ? null : turn; stopAtStep = step; } @@ -208,6 +215,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * * @param turn Expected turn number to compare with. 1-based. */ + @Override public void assertTurn(int turn) throws AssertionError { Assert.assertEquals("Turn numbers are not equal", turn, currentGame.getTurnNum()); } @@ -217,21 +225,28 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * * @param result Expected {@link GameResult} to compare with. */ + @Override public void assertResult(Player player, GameResult result) throws AssertionError { if (player.equals(playerA)) { GameResult actual = CardTestAPI.GameResult.DRAW; - if (currentGame.getWinner().equals("Player PlayerA is the winner")) { - actual = CardTestAPI.GameResult.WON; - } else if (currentGame.getWinner().equals("Player PlayerB is the winner")) { - actual = CardTestAPI.GameResult.LOST; + switch (currentGame.getWinner()) { + case "Player PlayerA is the winner": + actual = CardTestAPI.GameResult.WON; + break; + case "Player PlayerB is the winner": + actual = CardTestAPI.GameResult.LOST; + break; } Assert.assertEquals("Game results are not equal", result, actual); } else if (player.equals(playerB)) { GameResult actual = CardTestAPI.GameResult.DRAW; - if (currentGame.getWinner().equals("Player PlayerB is the winner")) { - actual = CardTestAPI.GameResult.WON; - } else if (currentGame.getWinner().equals("Player PlayerA is the winner")) { - actual = CardTestAPI.GameResult.LOST; + switch (currentGame.getWinner()) { + case "Player PlayerB is the winner": + actual = CardTestAPI.GameResult.WON; + break; + case "Player PlayerA is the winner": + actual = CardTestAPI.GameResult.LOST; + break; } Assert.assertEquals("Game results are not equal", result, actual); } @@ -243,6 +258,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param player {@link Player} to get life for comparison. * @param life Expected player's life to compare with. */ + @Override public void assertLife(Player player, int life) throws AssertionError { int actual = currentGame.getPlayer(player.getId()).getLife(); Assert.assertEquals("Life amounts are not equal", life, actual); @@ -265,6 +281,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param scope {@link mage.filter.Filter.ComparisonScope} Use ANY, if you want "at least one creature with given name should have specified p\t" * Use ALL, if you want "all creature with gived name should have specified p\t" */ + @Override public void assertPowerToughness(Player player, String cardName, int power, int toughness, Filter.ComparisonScope scope) throws AssertionError { int count = 0; @@ -298,6 +315,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP /** * {@inheritDoc} */ + @Override public void assertAbilities(Player player, String cardName, List abilities) throws AssertionError { int count = 0; @@ -326,6 +344,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param player {@link Player} which permanents should be counted. * @param count Expected count. */ + @Override public void assertPermanentCount(Player player, int count) throws AssertionError { int actualCount = 0; for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) { @@ -343,6 +362,7 @@ public abstract class CardTestAPIImpl extends MageTestBase implements CardTestAP * @param cardName Name of the cards that should be counted. * @param count Expected count. */ + @Override public void assertPermanentCount(Player player, String cardName, int count) throws AssertionError { int actualCount = 0; for (Permanent permanent : currentGame.getBattlefield().getAllPermanents()) { diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index c46ae3fa20a..dc059ca7cf6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -1,6 +1,8 @@ package org.mage.test.serverside.base.impl; import java.io.FileNotFoundException; +import java.util.List; +import java.util.UUID; import mage.abilities.Ability; import mage.cards.Card; import mage.cards.decks.Deck; @@ -18,20 +20,18 @@ import mage.filter.predicate.mageobject.NamePredicate; import mage.game.ExileZone; import mage.game.Game; import mage.game.GameException; +import mage.game.GameOptions; import mage.game.command.CommandObject; import mage.game.permanent.Permanent; import mage.game.permanent.PermanentCard; import mage.players.Player; import org.junit.Assert; +import org.junit.Before; import org.mage.test.player.TestPlayer; import org.mage.test.serverside.base.CardTestAPI; +import org.mage.test.serverside.base.CardTestAPI.GameResult; import org.mage.test.serverside.base.MageTestPlayerBase; -import java.util.List; -import java.util.UUID; -import mage.game.GameOptions; -import org.junit.Before; - /** * API for test initialization and asserting the test results. * @@ -213,7 +213,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * * @param player {@link Player} to remove all library cards from. */ - @Override public void removeAllCardsFromLibrary(TestPlayer player) { getCommands(player).put(Zone.LIBRARY, "clear"); } @@ -235,7 +234,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param player {@link Player} to add cards for. Use either playerA or playerB. * @param cardName Card name in string format. */ - @Override public void addCard(Zone gameZone, TestPlayer player, String cardName) { addCard(gameZone, player, cardName, 1, false); } @@ -248,7 +246,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param cardName Card name in string format. * @param count Amount of cards to be added. */ - @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count) { addCard(gameZone, player, cardName, count, false); } @@ -263,7 +260,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param tapped In case gameZone is Battlefield, determines whether permanent should be tapped. * In case gameZone is other than Battlefield, {@link IllegalArgumentException} is thrown */ - @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) { if (gameZone.equals(Zone.BATTLEFIELD)) { @@ -318,7 +314,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement * @param player {@link Player} to set life count for. * @param life Life count to set. */ - @Override public void setLife(TestPlayer player, int life) { getCommands(player).put(Zone.OUTSIDE, "life:" + String.valueOf(life)); } diff --git a/Mage/src/mage/abilities/effects/common/SacrificeEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeEffect.java index e03acac3325..ad24e45819a 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeEffect.java @@ -90,7 +90,6 @@ public class SacrificeEffect extends OneShotEffect{ // A spell or ability could have removed the only legal target this player // had, if thats the case this ability should fizzle. if (amount > 0 && target.canChoose(source.getSourceId(), player.getId(), game)) { - boolean abilityApplied = false; while (!target.isChosen() && target.canChoose(player.getId(), game) && player.isInGame()) { player.chooseTarget(Outcome.Sacrifice, target, source, game); } @@ -99,11 +98,11 @@ public class SacrificeEffect extends OneShotEffect{ Permanent permanent = game.getPermanent(target.getTargets().get(idx)); if ( permanent != null ) { - abilityApplied |= permanent.sacrifice(source.getSourceId(), game); + permanent.sacrifice(source.getSourceId(), game); } } - return abilityApplied; + return true; } return false; } diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 8a108c50aad..23561b34258 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -2625,7 +2625,7 @@ public abstract class GameImpl implements Game, Serializable { playerObject.abort(); } } - state.restore(restore); + state.restoreForRollBack(restore); // because restore uses the objects without copy each copy the state again gameStatesRollBack.put(getTurnNum(), state.copy()); executingRollback = true; diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index a29a353ee67..7d6d7eb7f8d 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -175,11 +175,14 @@ public class GameState implements Serializable, Copyable { this.permanentOrderNumber = state.permanentOrderNumber; } + public void restoreForRollBack(GameState state) { + restore(state); + this.turn = state.turn; + } + public void restore(GameState state) { this.activePlayerId = state.activePlayerId; this.priorityPlayerId = state.priorityPlayerId; - this.turn = state.turn; - this.stack = state.stack; this.command = state.command; this.exile = state.exile; diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index 603744d0655..c2f4a016acd 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -49,6 +49,7 @@ import mage.abilities.costs.AlternativeSourceCosts; import mage.abilities.costs.VariableCost; import mage.abilities.costs.mana.ManaCost; import mage.abilities.costs.mana.ManaCosts; +import mage.abilities.mana.ManaOptions; import mage.cards.Card; import mage.cards.Cards; import mage.cards.decks.Deck; @@ -377,8 +378,10 @@ public interface Player extends MageItem, Copyable { void phasing(Game game); void untap(Game game); + ManaOptions getManaAvailable(Game game); List getPlayable(Game game, boolean hidden); List getPlayableOptions(Ability ability, Game game); + Set getPlayableInHand(Game game); LinkedHashMap getUseableActivatedAbilities(MageObject object, Zone zone, Game game); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index e0864e3627b..a9fb708017f 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1089,7 +1089,6 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public boolean activateAbility(ActivatedAbility ability, Game game) { - getManaPool().setStock(); // needed for the "mana already in the pool has to be used manually" option boolean result; if (ability instanceof PassAbility) { pass(game); @@ -1156,7 +1155,7 @@ public abstract class PlayerImpl implements Player, Serializable { return true; } } - game.restoreState(bookmark, source.getRule()); + game.restoreState(bookmark, source.getRule()); // why restore is needed here? return false; } @@ -2149,7 +2148,8 @@ public abstract class PlayerImpl implements Player, Serializable { return blockers; } - protected ManaOptions getManaAvailable(Game game) { + @Override + public ManaOptions getManaAvailable(Game game) { ManaOptions available = new ManaOptions(); List> sourceWithoutCosts = new ArrayList<>(); @@ -2207,7 +2207,7 @@ public abstract class PlayerImpl implements Player, Serializable { } // returns only mana producers that require mana payment - protected List getAvailableManaProducersWithCost(Game game) { + public List getAvailableManaProducersWithCost(Game game) { List result = new ArrayList<>(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) { for (ManaAbility ability : permanent.getAbilities().getManaAbilities(Zone.BATTLEFIELD)) { From 1cf93e6c436f06f31e547a0337f851aab56d753f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 8 Jun 2015 19:46:22 +0200 Subject: [PATCH 078/102] Fixed AI test class. --- .../java/org/mage/test/player/TestPlayer.java | 16 +++++++++++++++- .../serverside/base/CardTestPlayerBaseAI.java | 4 +++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 4c7c0e855e2..4f99f385689 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -108,6 +108,7 @@ public class TestPlayer implements Player { private int maxCallsWithoutAction = 100; private int foundNoAction = 0; + private boolean AIPlayer; private final List actions = new ArrayList<>(); private final List choices = new ArrayList<>(); @@ -119,6 +120,7 @@ public class TestPlayer implements Player { public TestPlayer(ComputerPlayer computerPlayer) { this.computerPlayer = computerPlayer; + AIPlayer = false; } public TestPlayer(final TestPlayer testPlayer) { @@ -414,7 +416,11 @@ public class TestPlayer implements Player { } } } - computerPlayer.pass(game); + if (AIPlayer) { + computerPlayer.priority(game); + } else { + computerPlayer.pass(game); + } // check to prevent endless loops if (numberOfActions == actions.size()) { foundNoAction++; @@ -1809,4 +1815,12 @@ public class TestPlayer implements Player { computerPlayer.pickCard(cards, deck, draft); } + public void setAIPlayer(boolean AIPlayer) { + this.AIPlayer = AIPlayer; + } + + public boolean isAIPlayer() { + return AIPlayer; + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java index 2fa9998510f..2f50da22db0 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/CardTestPlayerBaseAI.java @@ -58,7 +58,9 @@ public abstract class CardTestPlayerBaseAI extends CardTestPlayerAPIImpl { @Override protected TestPlayer createPlayer(String name) { if (name.equals("PlayerA")) { - return new TestPlayer(new ComputerPlayer7("PlayerA", RangeOfInfluence.ONE, skill)); + TestPlayer testPlayer = new TestPlayer(new ComputerPlayer7("PlayerA", RangeOfInfluence.ONE, skill)); + testPlayer.setAIPlayer(true); + return testPlayer; } return super.createPlayer(name); } From f4f3083238f40f94c22031de4a653eb843217e2b Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Mon, 8 Jun 2015 14:24:00 -0400 Subject: [PATCH 079/102] Added a bunch of misc. cards: Ronom Unicorn, Shattering Pulse, Shivan Zombie, Vodalian Zombie, False Summoning, Searing Flesh, Preemptive Strike, Thundersong Trumpeter, Break Asunder, Compulsion. --- .../src/mage/sets/coldsnap/RonomUnicorn.java | 70 ++++++++++++++++++ .../src/mage/sets/exodus/ShatteringPulse.java | 65 +++++++++++++++++ .../src/mage/sets/invasion/ShivanZombie.java | 70 ++++++++++++++++++ .../mage/sets/invasion/VodalianZombie.java | 70 ++++++++++++++++++ .../sets/masterseditioniv/FalseSummoning.java | 52 +++++++++++++ .../src/mage/sets/onslaught/SearingFlesh.java | 60 +++++++++++++++ .../sets/portalsecondage/FalseSummoning.java | 61 ++++++++++++++++ .../portalthreekingdoms/PreemptiveStrike.java | 61 ++++++++++++++++ .../sets/ravnica/ThundersongTrumpeter.java | 71 ++++++++++++++++++ .../src/mage/sets/scourge/BreakAsunder.java | 66 +++++++++++++++++ .../src/mage/sets/torment/Compulsion.java | 73 +++++++++++++++++++ 11 files changed, 719 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/coldsnap/RonomUnicorn.java create mode 100644 Mage.Sets/src/mage/sets/exodus/ShatteringPulse.java create mode 100644 Mage.Sets/src/mage/sets/invasion/ShivanZombie.java create mode 100644 Mage.Sets/src/mage/sets/invasion/VodalianZombie.java create mode 100644 Mage.Sets/src/mage/sets/masterseditioniv/FalseSummoning.java create mode 100644 Mage.Sets/src/mage/sets/onslaught/SearingFlesh.java create mode 100644 Mage.Sets/src/mage/sets/portalsecondage/FalseSummoning.java create mode 100644 Mage.Sets/src/mage/sets/portalthreekingdoms/PreemptiveStrike.java create mode 100644 Mage.Sets/src/mage/sets/ravnica/ThundersongTrumpeter.java create mode 100644 Mage.Sets/src/mage/sets/scourge/BreakAsunder.java create mode 100644 Mage.Sets/src/mage/sets/torment/Compulsion.java diff --git a/Mage.Sets/src/mage/sets/coldsnap/RonomUnicorn.java b/Mage.Sets/src/mage/sets/coldsnap/RonomUnicorn.java new file mode 100644 index 00000000000..879e35fc1d4 --- /dev/null +++ b/Mage.Sets/src/mage/sets/coldsnap/RonomUnicorn.java @@ -0,0 +1,70 @@ +/* + * 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 mage.sets.coldsnap; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.target.TargetPermanent; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class RonomUnicorn extends CardImpl { + + public RonomUnicorn(UUID ownerId) { + super(ownerId, 16, "Ronom Unicorn", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{W}"); + this.expansionSetCode = "CSP"; + this.subtype.add("Unicorn"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Sacrifice Ronom Unicorn: Destroy target enchantment. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new SacrificeSourceCost()); + ability.addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); + this.addAbility(ability); + } + + public RonomUnicorn(final RonomUnicorn card) { + super(card); + } + + @Override + public RonomUnicorn copy() { + return new RonomUnicorn(this); + } +} diff --git a/Mage.Sets/src/mage/sets/exodus/ShatteringPulse.java b/Mage.Sets/src/mage/sets/exodus/ShatteringPulse.java new file mode 100644 index 00000000000..fe34e1c963d --- /dev/null +++ b/Mage.Sets/src/mage/sets/exodus/ShatteringPulse.java @@ -0,0 +1,65 @@ +/* + * 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 mage.sets.exodus; + +import java.util.UUID; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.BuybackAbility; +import mage.target.TargetPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class ShatteringPulse extends CardImpl { + + public ShatteringPulse(UUID ownerId) { + super(ownerId, 102, "Shattering Pulse", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{R}"); + this.expansionSetCode = "EXO"; + + // Buyback {3} + this.addAbility(new BuybackAbility("{3}")); + + // Destroy target artifact. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactPermanent())); + } + + public ShatteringPulse(final ShatteringPulse card) { + super(card); + } + + @Override + public ShatteringPulse copy() { + return new ShatteringPulse(this); + } +} diff --git a/Mage.Sets/src/mage/sets/invasion/ShivanZombie.java b/Mage.Sets/src/mage/sets/invasion/ShivanZombie.java new file mode 100644 index 00000000000..3c11fb51a77 --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/ShivanZombie.java @@ -0,0 +1,70 @@ +/* + * 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 mage.sets.invasion; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.keyword.ProtectionAbility; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class ShivanZombie extends CardImpl { + private static final FilterCard protectionFilter = new FilterCard("white"); + static { + protectionFilter.add(new ColorPredicate(ObjectColor.WHITE)); + } + + public ShivanZombie(UUID ownerId) { + super(ownerId, 271, "Shivan Zombie", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{B}{R}"); + this.expansionSetCode = "INV"; + this.subtype.add("Barbarian"); + this.subtype.add("Zombie"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Protection from white + this.addAbility(new ProtectionAbility(protectionFilter)); + } + + public ShivanZombie(final ShivanZombie card) { + super(card); + } + + @Override + public ShivanZombie copy() { + return new ShivanZombie(this); + } +} diff --git a/Mage.Sets/src/mage/sets/invasion/VodalianZombie.java b/Mage.Sets/src/mage/sets/invasion/VodalianZombie.java new file mode 100644 index 00000000000..e904e1a4d33 --- /dev/null +++ b/Mage.Sets/src/mage/sets/invasion/VodalianZombie.java @@ -0,0 +1,70 @@ +/* + * 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 mage.sets.invasion; + +import java.util.UUID; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.keyword.ProtectionAbility; +import mage.filter.FilterCard; +import mage.filter.predicate.mageobject.ColorPredicate; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class VodalianZombie extends CardImpl { + private static final FilterCard protectionFilter = new FilterCard("green"); + static { + protectionFilter.add(new ColorPredicate(ObjectColor.GREEN)); + } + + public VodalianZombie(UUID ownerId) { + super(ownerId, 286, "Vodalian Zombie", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{U}{B}"); + this.expansionSetCode = "INV"; + this.subtype.add("Merfolk"); + this.subtype.add("Zombie"); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Protection from green + this.addAbility(new ProtectionAbility(protectionFilter)); + } + + public VodalianZombie(final VodalianZombie card) { + super(card); + } + + @Override + public VodalianZombie copy() { + return new VodalianZombie(this); + } +} diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/FalseSummoning.java b/Mage.Sets/src/mage/sets/masterseditioniv/FalseSummoning.java new file mode 100644 index 00000000000..74c2c1a5aa5 --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/FalseSummoning.java @@ -0,0 +1,52 @@ +/* + * 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 mage.sets.masterseditioniv; + +import java.util.UUID; + +/** + * + * @author Jgod + */ +public class FalseSummoning extends mage.sets.portalsecondage.FalseSummoning { + + public FalseSummoning(UUID ownerId) { + super(ownerId); + this.cardNumber = 49; + this.expansionSetCode = "ME4"; + } + + public FalseSummoning(final FalseSummoning card) { + super(card); + } + + @Override + public FalseSummoning copy() { + return new FalseSummoning(this); + } +} diff --git a/Mage.Sets/src/mage/sets/onslaught/SearingFlesh.java b/Mage.Sets/src/mage/sets/onslaught/SearingFlesh.java new file mode 100644 index 00000000000..82fb9f8353d --- /dev/null +++ b/Mage.Sets/src/mage/sets/onslaught/SearingFlesh.java @@ -0,0 +1,60 @@ +/* + * 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 mage.sets.onslaught; + +import java.util.UUID; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.target.common.TargetOpponent; + +/** + * + * @author Jgod + */ +public class SearingFlesh extends CardImpl { + + public SearingFlesh(UUID ownerId) { + super(ownerId, 225, "Searing Flesh", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{6}{R}"); + this.expansionSetCode = "ONS"; + + // Searing Flesh deals 7 damage to target opponent. + this.getSpellAbility().addTarget(new TargetOpponent()); + this.getSpellAbility().addEffect(new DamageTargetEffect(7)); + } + + public SearingFlesh(final SearingFlesh card) { + super(card); + } + + @Override + public SearingFlesh copy() { + return new SearingFlesh(this); + } +} diff --git a/Mage.Sets/src/mage/sets/portalsecondage/FalseSummoning.java b/Mage.Sets/src/mage/sets/portalsecondage/FalseSummoning.java new file mode 100644 index 00000000000..8e52b122a37 --- /dev/null +++ b/Mage.Sets/src/mage/sets/portalsecondage/FalseSummoning.java @@ -0,0 +1,61 @@ +/* + * 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 mage.sets.portalsecondage; + +import java.util.UUID; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.filter.common.FilterCreatureSpell; +import mage.target.TargetSpell; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class FalseSummoning extends CardImpl { + + public FalseSummoning(UUID ownerId) { + super(ownerId, 40, "False Summoning", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{U}"); + this.expansionSetCode = "PO2"; + + // Counter target creature spell. + this.getSpellAbility().addTarget(new TargetSpell(new FilterCreatureSpell())); + this.getSpellAbility().addEffect(new CounterTargetEffect()); + } + + public FalseSummoning(final FalseSummoning card) { + super(card); + } + + @Override + public FalseSummoning copy() { + return new FalseSummoning(this); + } +} diff --git a/Mage.Sets/src/mage/sets/portalthreekingdoms/PreemptiveStrike.java b/Mage.Sets/src/mage/sets/portalthreekingdoms/PreemptiveStrike.java new file mode 100644 index 00000000000..3d5aa3ce4f0 --- /dev/null +++ b/Mage.Sets/src/mage/sets/portalthreekingdoms/PreemptiveStrike.java @@ -0,0 +1,61 @@ +/* + * 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 mage.sets.portalthreekingdoms; + +import java.util.UUID; +import mage.abilities.effects.common.CounterTargetEffect; +import mage.filter.common.FilterCreatureSpell; +import mage.target.TargetSpell; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class PreemptiveStrike extends CardImpl { + + public PreemptiveStrike(UUID ownerId) { + super(ownerId, 50, "Preemptive Strike", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{U}"); + this.expansionSetCode = "PTK"; + + // Counter target creature spell. + this.getSpellAbility().addTarget(new TargetSpell(new FilterCreatureSpell())); + this.getSpellAbility().addEffect(new CounterTargetEffect()); + } + + public PreemptiveStrike(final PreemptiveStrike card) { + super(card); + } + + @Override + public PreemptiveStrike copy() { + return new PreemptiveStrike(this); + } +} diff --git a/Mage.Sets/src/mage/sets/ravnica/ThundersongTrumpeter.java b/Mage.Sets/src/mage/sets/ravnica/ThundersongTrumpeter.java new file mode 100644 index 00000000000..c487030aac2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/ravnica/ThundersongTrumpeter.java @@ -0,0 +1,71 @@ +/* + * 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 mage.sets.ravnica; + +import java.util.UUID; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.common.combat.CantBlockTargetEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Jgod + */ +public class ThundersongTrumpeter extends CardImpl { + + public ThundersongTrumpeter(UUID ownerId) { + super(ownerId, 235, "Thundersong Trumpeter", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{R}{W}"); + this.expansionSetCode = "RAV"; + this.subtype.add("Human"); + this.subtype.add("Soldier"); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // {tap}: Target creature can't attack or block this turn. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CantBlockTargetEffect(Duration.EndOfTurn), new TapSourceCost()); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public ThundersongTrumpeter(final ThundersongTrumpeter card) { + super(card); + } + + @Override + public ThundersongTrumpeter copy() { + return new ThundersongTrumpeter(this); + } +} diff --git a/Mage.Sets/src/mage/sets/scourge/BreakAsunder.java b/Mage.Sets/src/mage/sets/scourge/BreakAsunder.java new file mode 100644 index 00000000000..a2772e6d133 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scourge/BreakAsunder.java @@ -0,0 +1,66 @@ +/* + * 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 mage.sets.scourge; + +import java.util.UUID; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.CyclingAbility; +import mage.filter.common.FilterArtifactOrEnchantmentPermanent; +import mage.target.TargetPermanent; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; + +/** + * + * @author Jgod + */ +public class BreakAsunder extends CardImpl { + + public BreakAsunder(UUID ownerId) { + super(ownerId, 113, "Break Asunder", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{2}{G}{G}"); + this.expansionSetCode = "SCG"; + + // Destroy target artifact or enchantment. + this.getSpellAbility().addEffect(new DestroyTargetEffect()); + this.getSpellAbility().addTarget(new TargetPermanent(new FilterArtifactOrEnchantmentPermanent())); + + // Cycling {2} + this.addAbility(new CyclingAbility(new ManaCostsImpl("{2}"))); + } + + public BreakAsunder(final BreakAsunder card) { + super(card); + } + + @Override + public BreakAsunder copy() { + return new BreakAsunder(this); + } +} diff --git a/Mage.Sets/src/mage/sets/torment/Compulsion.java b/Mage.Sets/src/mage/sets/torment/Compulsion.java new file mode 100644 index 00000000000..e2f7d90169e --- /dev/null +++ b/Mage.Sets/src/mage/sets/torment/Compulsion.java @@ -0,0 +1,73 @@ +/* + * 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 mage.sets.torment; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.DiscardTargetCost; +import mage.abilities.costs.common.SacrificeSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.DrawCardSourceControllerEffect; +import mage.filter.FilterCard; +import mage.target.common.TargetCardInHand; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; +import mage.constants.Zone; + +/** + * + * @author Jgod + */ +public class Compulsion extends CardImpl { + + public Compulsion(UUID ownerId) { + super(ownerId, 34, "Compulsion", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + this.expansionSetCode = "TOR"; + + // {1}{U}, Discard a card: Draw a card. + Ability ability1 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}{U}")); + ability1.addCost(new DiscardTargetCost(new TargetCardInHand(1, new FilterCard("a card")))); + this.addAbility(ability1); + + // {1}{U}, Sacrifice Compulsion: Draw a card. + Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl("{1}{U}")); + ability2.addCost(new SacrificeSourceCost()); + this.addAbility(ability2); + } + + public Compulsion(final Compulsion card) { + super(card); + } + + @Override + public Compulsion copy() { + return new Compulsion(this); + } +} From 256b93ffd82c2be8d998504dbb6aed06ef4a94cd Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 8 Jun 2015 22:02:46 +0200 Subject: [PATCH 080/102] * Sphinx Summoner - Fixed that the searched card was not revealed. --- Mage.Sets/src/mage/sets/conflux/SphinxSummoner.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/conflux/SphinxSummoner.java b/Mage.Sets/src/mage/sets/conflux/SphinxSummoner.java index aa6532ea9fa..ca99e4bcb24 100644 --- a/Mage.Sets/src/mage/sets/conflux/SphinxSummoner.java +++ b/Mage.Sets/src/mage/sets/conflux/SphinxSummoner.java @@ -45,6 +45,7 @@ import mage.target.common.TargetCardInLibrary; * @author Loki */ public class SphinxSummoner extends CardImpl { + private static final FilterCard filter = new FilterCard("artifact creature card"); static { @@ -62,7 +63,7 @@ public class SphinxSummoner extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Sphinx Summoner enters the battlefield, you may search your library for an artifact creature card, reveal it, and put it into your hand. If you do, shuffle your library. - this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter)), true)); + this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter), true, false), true)); } public SphinxSummoner(final SphinxSummoner card) { From 21e2066bba6ea05fc7a97e998affa49f623fb0e4 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 8 Jun 2015 22:51:48 +0200 Subject: [PATCH 081/102] * Akron Legionnaire - Fixed not correct working restrict ability. --- .../java/mage/client/game/PlayAreaPanel.java | 2 +- .../mage/sets/legends/AkronLegionnaire.java | 31 +++----- .../mage/test/cards/mana/ManaFlareTest.java | 2 +- .../cards/restriction/CantAttackTest.java | 71 +++++++++++++++++++ 4 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantAttackTest.java diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java index 4ffc7cd5f85..8f5a31791f7 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java @@ -89,6 +89,7 @@ public class PlayAreaPanel extends javax.swing.JPanel { * @param options */ public PlayAreaPanel(PlayerView player, BigCard bigCard, UUID gameId, int priorityTime, GamePanel gamePanel, PlayAreaPanelOptions options) { + this.gamePanel = gamePanel; this.options = options; initComponents(); setOpaque(false); @@ -101,7 +102,6 @@ public class PlayAreaPanel extends javax.swing.JPanel { addPopupMenuWatcher(); } this.add(popupMenu); - this.gamePanel = gamePanel; init(player, bigCard, gameId, priorityTime); update(player); } diff --git a/Mage.Sets/src/mage/sets/legends/AkronLegionnaire.java b/Mage.Sets/src/mage/sets/legends/AkronLegionnaire.java index 2a9d35dc5c8..307b1ff9954 100644 --- a/Mage.Sets/src/mage/sets/legends/AkronLegionnaire.java +++ b/Mage.Sets/src/mage/sets/legends/AkronLegionnaire.java @@ -30,8 +30,8 @@ package mage.sets.legends; import java.util.UUID; import mage.MageInt; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; import mage.abilities.effects.common.combat.CantAttackAllAnyPlayerEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; @@ -39,9 +39,10 @@ import mage.constants.Rarity; import mage.constants.TargetController; import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.Predicate; +import mage.filter.predicate.Predicates; +import mage.filter.predicate.mageobject.CardTypePredicate; +import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.permanent.ControllerPredicate; -import mage.game.Game; /** * @@ -53,7 +54,8 @@ public class AkronLegionnaire extends CardImpl { static { filter.add(new ControllerPredicate(TargetController.YOU)); - filter.add(new AkronLegionairePredicate()); + filter.add(Predicates.not(new NamePredicate("Akron Legionnaire"))); + filter.add(Predicates.not(new CardTypePredicate(CardType.ARTIFACT))); } public AkronLegionnaire(UUID ownerId) { @@ -65,7 +67,9 @@ public class AkronLegionnaire extends CardImpl { this.toughness = new MageInt(4); // Except for creatures named Akron Legionnaire and artifact creatures, creatures you control can't attack. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantAttackAllAnyPlayerEffect(Duration.WhileOnBattlefield, filter))); + Effect effect = new CantAttackAllAnyPlayerEffect(Duration.WhileOnBattlefield, filter); + effect.setText("Except for creatures named Akron Legionnaire and artifact creatures, creatures you control can't attack"); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); } @@ -78,20 +82,3 @@ public class AkronLegionnaire extends CardImpl { return new AkronLegionnaire(this); } } - -class AkronLegionairePredicate implements Predicate { - - public AkronLegionairePredicate() { - } - - @Override - public boolean apply(Card input, Game game) { - return !input.getCardType().contains(CardType.ARTIFACT) - || !input.getName().contains("Akron Legionaire"); - } - - @Override - public String toString() { - return ""; - } -} \ No newline at end of file diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaFlareTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaFlareTest.java index 823b579cce4..f81242ffc2b 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaFlareTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/mana/ManaFlareTest.java @@ -41,7 +41,7 @@ public class ManaFlareTest extends CardTestPlayerBase { @Test public void testIsland() { - // {T}: Add one mana of any color to your mana pool. Spend this mana only to cast a multicolored spell. + // Whenever a player taps a land for mana, that player adds one mana to his or her mana pool of any type that land produced. addCard(Zone.BATTLEFIELD, playerA, "Mana Flare", 1); addCard(Zone.BATTLEFIELD, playerA, "Island", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantAttackTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantAttackTest.java new file mode 100644 index 00000000000..479e09910a9 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/restriction/CantAttackTest.java @@ -0,0 +1,71 @@ +/* + * 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.cards.restriction; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class CantAttackTest extends CardTestPlayerBase { + + /** + * Tests "If all other elves get the Forestwalk ability and can't be blockt from creatures whose controler has a forest in game" + */ + + @Test + public void testAttack() { + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); // 2/2 + addCard(Zone.BATTLEFIELD, playerA, "Myr Enforcer"); // 4/4 + + addCard(Zone.BATTLEFIELD, playerB, "Akron Legionnaire"); // 8/4 + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); // 2/2 + addCard(Zone.BATTLEFIELD, playerB, "Myr Enforcer"); // 4/4 + + attack(2, playerB, "Akron Legionnaire"); + attack(2, playerB, "Silvercoat Lion"); + attack(2, playerB, "Myr Enforcer"); + + attack(3, playerA, "Silvercoat Lion"); + attack(3, playerA, "Myr Enforcer"); + + setStopAt(3, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 8); // 8 + 4 + assertLife(playerB, 14); // 4 + 2 + + } + +} + From 597211fcb155963bf119835459e685b0f338f003 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Tue, 9 Jun 2015 10:54:31 -0400 Subject: [PATCH 082/102] Readme changes: fixed spelling error, improved some grammatical things, and added a couple more resources for developers. --- readme.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index 902013827b2..5cd15080014 100644 --- a/readme.md +++ b/readme.md @@ -1,8 +1,8 @@ # XMage - Magic, Another Game Engine -XMage allows you to play magic against one or more online players or computer opponents. It includes full rules enforcement for over **10,000** unique cards (nearly 20.000 counting all cards from different editions). Starting with Eventide, all regular sets have nearly all the cards implemented ([see here in detail which cards are implemented](http://ct-magefree.rhcloud.com/stats)). +XMage allows you to play magic against one or more online players or computer opponents. It includes full rules enforcement for over **10,000** unique cards (nearly 20.000 counting all cards from different editions). Starting with Eventide, all regular sets have nearly all the cards implemented ([a detailed overview](http://ct-magefree.rhcloud.com/stats)). -There are public servers where you can play XMage against other players. Apart from this, you can also host your own server to play against the AI and/or your friends. +There are public servers where you can play XMage against other players. You can also host your own server to play against the AI and/or your friends. You can visit the XMage forum [here](http://www.slightlymagic.net/forum/viewforum.php?f=70). @@ -10,25 +10,26 @@ You can visit the XMage forum [here](http://www.slightlymagic.net/forum/viewforu * Deck editor to build your desired decks. * There is a simple computer AI opponent available. * You can play either a two player duel or a multiplayer free-for-all game with up to 10 players. -* Commander format(also up to 10 players). +* Commander format (also up to 10 players). * Tiny Leaders duels. -* There are two tournament types supported witch can be played with up to 16 players: +* There are two tournament types supported, which can be played with up to 16 players: * Elimination or swiss type handling * Booster (also Cube) draft tournaments (4-16) * Sealed (also from Cube) tournaments (2-16) ## Installation -Install the XMage Launcher to download and install always the latest XMage release from [here](http://XMage.de). +Download and install the [latest XMage release](http://XMage.de). You will need to have the [Java Runtime Environment](http://java.com/en/) Version 7 or later. -Here you can find a log of the latest changes: [Release changes] (http://github.com/magefree/mage/wiki/Release-changes) - -(look here for more in detail description what to do: http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=13632) +Look [here](http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=13632) for more detailed instructions. +[Here](http://github.com/magefree/mage/wiki/Release-changes) you can find a log of the latest changes. ## Developer -If you are interesting in developing XMage here are a couple of links that might interest you: +If you are interested in developing XMage, here are some useful resources: * [Developer Getting Started](http://github.com/magefree/mage/wiki/Developer-Getting-Started) * [Developer Notes](http://github.com/magefree/mage/wiki/Developer-Notes) -* [Developer Testing Tools] (http://github.com/magefree/mage/wiki/Developer-Testing-Tools) +* [Developer Testing Tools](http://github.com/magefree/mage/wiki/Developer-Testing-Tools) * [Double Faced Cards](http://github.com/magefree/mage/wiki/Double-Faced-Cards) +* [Card Requests](http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=4554) +* [Tournament Relevant Card Requests](http://www.slightlymagic.net/forum/viewtopic.php?f=70&t=14062) \ No newline at end of file From 119422750c0f9c1a1825e77292e7e54d1e8a0906 Mon Sep 17 00:00:00 2001 From: Justin Godesky Date: Tue, 9 Jun 2015 12:08:04 -0400 Subject: [PATCH 083/102] Minor readme changes. --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 5cd15080014..a7ee59046a3 100644 --- a/readme.md +++ b/readme.md @@ -1,6 +1,6 @@ # XMage - Magic, Another Game Engine -XMage allows you to play magic against one or more online players or computer opponents. It includes full rules enforcement for over **10,000** unique cards (nearly 20.000 counting all cards from different editions). Starting with Eventide, all regular sets have nearly all the cards implemented ([a detailed overview](http://ct-magefree.rhcloud.com/stats)). +XMage allows you to play Magic against one or more online players or computer opponents. It includes full rules enforcement for over **10,000** unique cards (nearly 20,000 counting all cards from different editions). Starting with Eventide, all regular sets have nearly all the cards implemented ([detailed overview](http://ct-magefree.rhcloud.com/stats)). There are public servers where you can play XMage against other players. You can also host your own server to play against the AI and/or your friends. From c27ef5ee97761701c90bc730a7fac6f104a436e6 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 10 Jun 2015 01:24:38 +0200 Subject: [PATCH 084/102] * Started to fix color setting for cards (e.g. Painter's Serverant) #408 (not completely finished yet). --- Mage.Common/src/mage/view/CardView.java | 6 +- .../src/mage/view/StackAbilityView.java | 4 +- .../sets/alarareborn/GloryscaleViashino.java | 2 +- .../sets/alarareborn/KnightOfNewAlara.java | 2 +- .../src/mage/sets/apocalypse/FireIce.java | 5 - .../mage/sets/apocalypse/IllusionReality.java | 5 - .../src/mage/sets/apocalypse/LifeDeath.java | 5 - .../src/mage/sets/apocalypse/NightDay.java | 5 - .../src/mage/sets/apocalypse/OrderChaos.java | 5 - .../mage/sets/avacynrestored/DreadSlaver.java | 2 +- .../EmptyShrineKannushi.java | 2 +- .../championsofkamigawa/AkkiLavarunner.java | 2 +- .../championsofkamigawa/KondasBanner.java | 2 +- .../mage/sets/commander2013/RoughTumble.java | 4 - .../commander2014/NecromanticSelection.java | 2 +- .../mage/sets/conflux/FiligreeFracture.java | 2 +- .../mage/sets/conflux/KederektParasite.java | 2 +- .../mage/sets/darksteel/MycosynthLattice.java | 14 +- .../src/mage/sets/dissension/HideSeek.java | 8 - .../src/mage/sets/dissension/OddsEnds.java | 8 - .../sets/dissension/PillarOfTheParuns.java | 2 +- .../sets/dissension/ResearchDevelopment.java | 8 - .../mage/sets/dissension/SupplyDemand.java | 8 - .../src/mage/sets/dragonsmaze/AliveWell.java | 5 - .../mage/sets/dragonsmaze/ArmedDangerous.java | 5 - .../src/mage/sets/dragonsmaze/BeckCall.java | 8 - .../src/mage/sets/dragonsmaze/DownDirty.java | 5 - .../src/mage/sets/dragonsmaze/FarAway.java | 5 - .../src/mage/sets/dragonsmaze/FleshBlood.java | 8 - .../src/mage/sets/dragonsmaze/GiveTake.java | 5 - .../src/mage/sets/dragonsmaze/ProfitLoss.java | 5 - .../mage/sets/dragonsmaze/ProtectServe.java | 5 - .../mage/sets/dragonsmaze/ReadyWilling.java | 8 - .../mage/sets/dragonsmaze/ToilTrouble.java | 5 - .../src/mage/sets/dragonsmaze/TurnBurn.java | 7 - .../src/mage/sets/dragonsmaze/WearTear.java | 5 - .../dragonsoftarkir/DisplayOfDominance.java | 2 +- .../src/mage/sets/eventide/BloomTender.java | 10 +- .../src/mage/sets/eventide/IndigoFaerie.java | 2 +- .../mage/sets/fifthedition/Hydroblast.java | 4 +- .../src/mage/sets/fifthedition/Justice.java | 2 +- .../src/mage/sets/futuresight/DryadArbor.java | 2 +- .../mage/sets/futuresight/GlitteringWish.java | 2 +- .../sets/gatecrash/LazavDimirMastermind.java | 2 +- Mage.Sets/src/mage/sets/iceage/Pyroblast.java | 4 +- .../mage/sets/innistrad/EssenceOfTheWild.java | 2 +- .../sets/innistrad/GrimoireOfTheDead.java | 2 +- .../mage/sets/invasion/AssaultBattery.java | 5 - .../sets/journeyintonyx/HallOfTriumph.java | 2 +- .../sets/khansoftarkir/GhostfireBlade.java | 2 +- .../SarkhanTheDragonspeaker.java | 2 +- .../src/mage/sets/legends/Abomination.java | 8 +- .../mage/sets/limitedalpha/CrystalRod.java | 2 +- .../src/mage/sets/limitedalpha/IronStar.java | 2 +- .../src/mage/sets/limitedalpha/IvoryCup.java | 2 +- .../mage/sets/limitedalpha/ThroneOfBone.java | 2 +- .../mage/sets/limitedalpha/WoodenSphere.java | 2 +- Mage.Sets/src/mage/sets/limitedbeta/Fork.java | 2 +- .../src/mage/sets/magic2010/DreadWarlock.java | 2 +- .../mage/sets/magic2010/RiseFromTheGrave.java | 2 +- .../mage/sets/magic2010/VampireNocturnus.java | 2 +- .../src/mage/sets/magic2011/FireServant.java | 2 +- .../mage/sets/magic2012/ChandrasPhoenix.java | 2 +- .../sets/magic2014/FiendslayerPaladin.java | 4 +- .../sets/magic2015/AvacynGuardianAngel.java | 4 +- .../sets/magic2015/PolymorphistsJest.java | 12 +- .../mage/sets/magic2015/SoulOfRavnica.java | 10 +- .../src/mage/sets/mirrodin/ChromeMox.java | 2 +- .../modernmasters2015/KarplusanStrider.java | 2 +- .../src/mage/sets/newphyrexia/CagedSun.java | 2 +- .../mage/sets/ninthedition/Deathgazer.java | 4 +- .../mage/sets/odyssey/EarnestFellowship.java | 4 +- .../mage/sets/odyssey/TestamentOfFaith.java | 4 +- .../src/mage/sets/planarchaos/BoomBust.java | 4 - .../src/mage/sets/planarchaos/DeadGone.java | 4 - .../mage/sets/planarchaos/LifeAndLimb.java | 2 +- .../mage/sets/planeshift/MeteorCrater.java | 2 +- .../src/mage/sets/planeshift/ShiftingSky.java | 15 +- .../mage/sets/ravnica/CrownOfConvergence.java | 2 +- .../mage/sets/ravnica/DimirDoppelganger.java | 2 +- .../src/mage/sets/ravnica/LeaveNoTrace.java | 4 +- .../mage/sets/ravnica/RallyTheRighteous.java | 8 +- .../mage/sets/returntoravnica/CivicSaber.java | 2 +- .../sets/returntoravnica/GraveBetrayal.java | 2 +- .../mage/sets/returntoravnica/LobberCrew.java | 2 +- .../sets/returntoravnica/Pyroconvergence.java | 2 +- .../returntoravnica/TabletOfTheGuilds.java | 4 +- .../mage/sets/riseoftheeldrazi/AllIsDust.java | 2 +- .../riseoftheeldrazi/CurseOfWizardry.java | 2 +- .../sets/riseoftheeldrazi/EldraziTemple.java | 2 +- .../riseoftheeldrazi/ZulaportEnforcer.java | 2 +- .../sets/scarsofmirrodin/GrandArchitect.java | 10 +- .../scarsofmirrodin/LiegeOfTheTangle.java | 2 +- .../QuicksilverGargantuan.java | 2 +- .../mage/sets/seventhedition/DarkestHour.java | 2 +- .../mage/sets/shadowmoor/ConsignToDream.java | 4 +- .../src/mage/sets/shadowmoor/Gloomlance.java | 4 +- .../sets/shadowmoor/GloomwidowsFeast.java | 4 +- .../sets/shadowmoor/IlluminatedFolio.java | 10 +- .../sets/shadowmoor/InquisitorsSnare.java | 4 +- .../sets/shadowmoor/LureboundScarecrow.java | 2 +- .../sets/shadowmoor/OonaQueenOfTheFae.java | 2 +- .../mage/sets/shadowmoor/PaintersServant.java | 51 ++++-- .../src/mage/sets/shadowmoor/Scrapbasket.java | 10 +- .../src/mage/sets/tempest/Grindstone.java | 4 +- .../src/mage/sets/tempest/LightOfDay.java | 2 +- .../mage/sets/tenthedition/AngelsFeather.java | 2 +- .../mage/sets/tenthedition/DragonsClaw.java | 2 +- .../mage/sets/tenthedition/KrakensEye.java | 2 +- .../mage/sets/tenthedition/WurmsTooth.java | 2 +- .../timeshifted/SolkanarTheSwampKing.java | 2 +- .../mage/sets/timespiral/AncestralVision.java | 2 +- .../mage/sets/timespiral/GauntletOfPower.java | 2 +- .../sets/timespiral/GhostflameSliver.java | 2 +- .../src/mage/sets/urzaslegacy/ThranLens.java | 2 +- .../src/mage/sets/urzassaga/Bereavement.java | 2 +- .../src/mage/sets/urzassaga/Persecute.java | 2 +- .../mage/sets/urzassaga/TitaniasChosen.java | 2 +- .../vintagemasters/VolrathsShapeshifter.java | 2 +- .../src/mage/sets/visions/ElephantGrass.java | 4 +- .../mage/sets/worldwake/KorFirewalker.java | 2 +- .../mage/sets/worldwake/PermafrostTrap.java | 2 +- .../mage/sets/worldwake/RefractionTrap.java | 2 +- .../src/mage/sets/worldwake/RicochetTrap.java | 2 +- .../src/mage/sets/worldwake/SlingbowTrap.java | 2 +- .../src/mage/sets/zendikar/ChandraAblaze.java | 2 +- .../sets/zendikar/IonaShieldOfEmeria.java | 2 +- .../mage/test/AI/basic/CastCreaturesTest.java | 30 ++++ .../cards/continuous/PaintersServantTest.java | 157 ++++++++++++++++++ Mage/src/mage/MageObject.java | 2 +- Mage/src/mage/MageObjectImpl.java | 2 +- .../abilities/effects/common/CopyEffect.java | 2 +- .../effects/common/CopyTokenEffect.java | 2 +- .../AddCardColorAttachedEffect.java | 10 +- .../BecomesBasicLandEnchantedEffect.java | 10 +- .../continuous/BecomesColorTargetEffect.java | 2 +- .../continuous/BecomesCreatureAllEffect.java | 4 +- .../BecomesCreatureAttachedEffect.java | 14 +- .../BecomesCreatureSourceEffect.java | 4 +- .../BecomesCreatureTargetEffect.java | 14 +- .../BecomesFaceDownCreatureAllEffect.java | 2 +- .../BecomesFaceDownCreatureEffect.java | 2 +- .../SetCardColorAttachedEffect.java | 2 +- .../continuous/SetCardColorSourceEffect.java | 2 +- .../abilities/keyword/ConvokeAbility.java | 2 +- .../mage/abilities/keyword/FearAbility.java | 2 +- .../abilities/keyword/IntimidateAbility.java | 2 +- .../mage/abilities/keyword/MorphAbility.java | 4 +- .../abilities/keyword/TransformAbility.java | 2 +- Mage/src/mage/cards/Card.java | 3 +- Mage/src/mage/cards/CardImpl.java | 14 +- Mage/src/mage/cards/repository/CardInfo.java | 10 +- .../predicate/mageobject/ColorPredicate.java | 2 +- .../mageobject/ColorlessPredicate.java | 2 +- .../mageobject/MonocoloredPredicate.java | 2 +- .../mageobject/MulticoloredPredicate.java | 2 +- .../SharesColorWithSourcePredicate.java | 2 +- Mage/src/mage/game/CardAttribute.java | 29 ++++ Mage/src/mage/game/GameState.java | 16 +- Mage/src/mage/game/command/Commander.java | 4 +- Mage/src/mage/game/command/Emblem.java | 2 +- .../mage/game/permanent/PermanentCard.java | 2 +- .../mage/game/permanent/PermanentImpl.java | 9 +- .../mage/game/permanent/PermanentToken.java | 2 +- Mage/src/mage/game/stack/Spell.java | 4 +- Mage/src/mage/game/stack/StackAbility.java | 2 +- Mage/src/mage/util/GameLog.java | 2 +- .../util/functions/CopyTokenFunction.java | 2 +- Mage/src/mage/util/trace/TraceUtil.java | 2 +- 169 files changed, 515 insertions(+), 395 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java create mode 100644 Mage/src/mage/game/CardAttribute.java diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 5829fa3a3bf..d0845cd7ae6 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -237,7 +237,7 @@ public class CardView extends SimpleCardView { this.cardTypes = card.getCardType(); this.subTypes = card.getSubtype(); this.superTypes = card.getSupertype(); - this.color = card.getColor(); + this.color = card.getColor(game); this.canTransform = card.canTransform(); this.flipCard = card.isFlipCard(); this.faceDown = game != null ? card.isFaceDown(game) : false; @@ -313,7 +313,7 @@ public class CardView extends SimpleCardView { this.cardTypes = object.getCardType(); this.subTypes = object.getSubtype(); this.superTypes = object.getSupertype(); - this.color = object.getColor(); + this.color = object.getColor(null); this.manaCost = object.getManaCost().getSymbols(); this.convertedManaCost = object.getManaCost().convertedManaCost(); if (object instanceof PermanentToken) { @@ -421,7 +421,7 @@ public class CardView extends SimpleCardView { this.cardTypes = token.getCardType(); this.subTypes = token.getSubtype(); this.superTypes = token.getSupertype(); - this.color = token.getColor(); + this.color = token.getColor(null); this.manaCost = token.getManaCost().getSymbols(); this.rarity = Rarity.NA; this.type = token.getTokenType(); diff --git a/Mage.Common/src/mage/view/StackAbilityView.java b/Mage.Common/src/mage/view/StackAbilityView.java index b8a08b07692..8cb82fa2ffe 100644 --- a/Mage.Common/src/mage/view/StackAbilityView.java +++ b/Mage.Common/src/mage/view/StackAbilityView.java @@ -64,12 +64,12 @@ public class StackAbilityView extends CardView { this.cardTypes = ability.getCardType(); this.subTypes = ability.getSubtype(); this.superTypes = ability.getSupertype(); - this.color = ability.getColor(); + this.color = ability.getColor(game); this.manaCost = ability.getManaCost().getSymbols(); this.cardTypes = ability.getCardType(); this.subTypes = ability.getSubtype(); this.superTypes = ability.getSupertype(); - this.color = ability.getColor(); + this.color = ability.getColor(game); this.manaCost = ability.getManaCost().getSymbols(); this.power = ability.getPower().toString(); this.toughness = ability.getToughness().toString(); diff --git a/Mage.Sets/src/mage/sets/alarareborn/GloryscaleViashino.java b/Mage.Sets/src/mage/sets/alarareborn/GloryscaleViashino.java index 87a8081830f..46d20e93bba 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/GloryscaleViashino.java +++ b/Mage.Sets/src/mage/sets/alarareborn/GloryscaleViashino.java @@ -89,7 +89,7 @@ class GloryscaleViashinoAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isMulticolored() && event.getPlayerId().equals(getControllerId())) { + if (spell != null && spell.getColor(game).isMulticolored() && event.getPlayerId().equals(getControllerId())) { return true; } } diff --git a/Mage.Sets/src/mage/sets/alarareborn/KnightOfNewAlara.java b/Mage.Sets/src/mage/sets/alarareborn/KnightOfNewAlara.java index 8505d0f946e..51ba166beb6 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/KnightOfNewAlara.java +++ b/Mage.Sets/src/mage/sets/alarareborn/KnightOfNewAlara.java @@ -102,7 +102,7 @@ class KnightOfNewAlaraEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { for (Permanent creature : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { if (creature != null && !creature.getId().equals(source.getSourceId())) { - int colors = creature.getColor().getColorCount(); + int colors = creature.getColor(game).getColorCount(); creature.addPower(colors); creature.addToughness(colors); } diff --git a/Mage.Sets/src/mage/sets/apocalypse/FireIce.java b/Mage.Sets/src/mage/sets/apocalypse/FireIce.java index 7f0394cdcd8..18f2d907653 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/FireIce.java +++ b/Mage.Sets/src/mage/sets/apocalypse/FireIce.java @@ -51,12 +51,8 @@ public class FireIce extends SplitCard { super(ownerId, 128, "Fire", "Ice", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{R}", "{1}{U}", false); this.expansionSetCode = "APC"; - this.color.setBlue(true); - this.color.setRed(true); - // Fire // Fire deals 2 damage divided as you choose among one or two target creatures and/or players. - getLeftHalfCard().getColor().setRed(true); Effect effect = new DamageMultiEffect(2); effect.setText("Fire deals 2 damage divided as you choose among one or two target creatures and/or players"); getLeftHalfCard().getSpellAbility().addEffect(effect); @@ -65,7 +61,6 @@ public class FireIce extends SplitCard { // Ice // Tap target permanent. // Draw a card. - getRightHalfCard().getColor().setBlue(true); getRightHalfCard().getSpellAbility().addEffect(new TapTargetEffect()); getRightHalfCard().getSpellAbility().addTarget(new TargetPermanent()); getRightHalfCard().getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); diff --git a/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java b/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java index 6a875b15cb1..af059572f70 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java +++ b/Mage.Sets/src/mage/sets/apocalypse/IllusionReality.java @@ -49,19 +49,14 @@ public class IllusionReality extends SplitCard { super(ownerId, 129, "Illusion", "Reality", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{U}", "{2}{G}", false); this.expansionSetCode = "APC"; - this.color.setBlue(true); - this.color.setGreen(true); - // Illusion // Target spell or permanent becomes the color of your choice until end of turn. - getLeftHalfCard().getColor().setBlue(true); getLeftHalfCard().getSpellAbility().addEffect(new BecomesColorTargetEffect(Duration.EndOfTurn)); Target target = new TargetSpellOrPermanent(); getLeftHalfCard().getSpellAbility().addTarget(target); // Reality // Destroy target artifact. - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addTarget(new TargetArtifactPermanent()); getRightHalfCard().getSpellAbility().addEffect(new DestroyTargetEffect()); } diff --git a/Mage.Sets/src/mage/sets/apocalypse/LifeDeath.java b/Mage.Sets/src/mage/sets/apocalypse/LifeDeath.java index 74b76951c09..1db52a16d06 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/LifeDeath.java +++ b/Mage.Sets/src/mage/sets/apocalypse/LifeDeath.java @@ -59,18 +59,13 @@ public class LifeDeath extends SplitCard { super(ownerId, 130, "Life", "Death", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{G}", "{1}{B}", false); this.expansionSetCode = "APC"; - this.color.setGreen(true); - this.color.setBlack(true); - // Life // All lands you control become 1/1 creatures until end of turn. They're still lands. - getLeftHalfCard().getColor().setGreen(true); getLeftHalfCard().getSpellAbility().addEffect(new BecomesCreatureAllEffect(new LifeLandToken(), "lands", new FilterControlledLandPermanent("lands you control"), Duration.EndOfTurn)); // Death // Return target creature card from your graveyard to the battlefield. You lose life equal to its converted mana cost. - getRightHalfCard().getColor().setBlack(true); Target target = new TargetCardInYourGraveyard(1, new FilterCreatureCard("creature card from your graveyard")); getRightHalfCard().getSpellAbility().addTarget(target); getRightHalfCard().getSpellAbility().addEffect(new DeathEffect()); diff --git a/Mage.Sets/src/mage/sets/apocalypse/NightDay.java b/Mage.Sets/src/mage/sets/apocalypse/NightDay.java index 1a29b3bfe59..51889bf52be 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/NightDay.java +++ b/Mage.Sets/src/mage/sets/apocalypse/NightDay.java @@ -59,18 +59,13 @@ public class NightDay extends SplitCard { super(ownerId, 131, "Night", "Day", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{B}", "{2}{W}", false); this.expansionSetCode = "APC"; - this.color.setBlack(true); - this.color.setWhite(true); - // Night // Target creature gets -1/-1 until end of turn. - getLeftHalfCard().getColor().setBlack(true); getLeftHalfCard().getSpellAbility().addEffect(new BoostTargetEffect(-1,-1,Duration.EndOfTurn)); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Day // Creatures target player controls get +1/+1 until end of turn. - getRightHalfCard().getColor().setWhite(true); getRightHalfCard().getSpellAbility().addTarget(new TargetPlayer()); getRightHalfCard().getSpellAbility().addEffect(new DayEffect()); diff --git a/Mage.Sets/src/mage/sets/apocalypse/OrderChaos.java b/Mage.Sets/src/mage/sets/apocalypse/OrderChaos.java index af280d76ead..d4e7afbe97e 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/OrderChaos.java +++ b/Mage.Sets/src/mage/sets/apocalypse/OrderChaos.java @@ -51,19 +51,14 @@ public class OrderChaos extends SplitCard { super(ownerId, 132, "Order", "Chaos", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{3}{W}", "{2}{R}", false); this.expansionSetCode = "APC"; - this.color.setWhite(true); - this.color.setRed(true); - // Order // Exile target attacking creature. - getLeftHalfCard().getColor().setWhite(true); getLeftHalfCard().getSpellAbility().addEffect(new ExileTargetEffect()); Target target = new TargetAttackingCreature(); getLeftHalfCard().getSpellAbility().addTarget(target); // Chaos // Creatures can't block this turn. - getRightHalfCard().getColor().setRed(true); getRightHalfCard().getSpellAbility().addEffect(new CantBlockAllEffect(new FilterCreaturePermanent("Creatures"), Duration.EndOfTurn)); } diff --git a/Mage.Sets/src/mage/sets/avacynrestored/DreadSlaver.java b/Mage.Sets/src/mage/sets/avacynrestored/DreadSlaver.java index 8dd0ecbfcd2..ff086a81a0f 100644 --- a/Mage.Sets/src/mage/sets/avacynrestored/DreadSlaver.java +++ b/Mage.Sets/src/mage/sets/avacynrestored/DreadSlaver.java @@ -138,7 +138,7 @@ class DreadSlaverContiniousEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - creature.getColor().setBlack(true); + creature.getColor(game).setBlack(true); } break; } diff --git a/Mage.Sets/src/mage/sets/betrayersofkamigawa/EmptyShrineKannushi.java b/Mage.Sets/src/mage/sets/betrayersofkamigawa/EmptyShrineKannushi.java index 13b8dc0930b..56805bbc971 100644 --- a/Mage.Sets/src/mage/sets/betrayersofkamigawa/EmptyShrineKannushi.java +++ b/Mage.Sets/src/mage/sets/betrayersofkamigawa/EmptyShrineKannushi.java @@ -93,7 +93,7 @@ class EmptyShrineKannushiProtectionAbility extends ProtectionAbility { public boolean canTarget(MageObject source, Game game) { ObjectColor color = new ObjectColor(); for (Permanent permanent: game.getBattlefield().getAllActivePermanents(controllerId)) { - ObjectColor permanentColor = permanent.getColor(); + ObjectColor permanentColor = permanent.getColor(game); if (permanentColor.isColorless()) { continue; } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java b/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java index c30235ab5f1..f0edd96bc58 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java @@ -126,7 +126,7 @@ class TokTokVolcanoBornEffect extends ReplacementEffectImpl { @Override public boolean applies(GameEvent event, Ability source, Game game) { Card card = game.getCard(event.getSourceId()); - if (card != null && card.getColor().isRed()) { + if (card != null && card.getColor(game).isRed()) { return true; } return false; diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java index 251f4c4d820..14109095917 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java @@ -155,7 +155,7 @@ class KondasBannerColorBoostEffect extends BoostAllEffect { if (equipment != null && equipment.getAttachedTo() != null) { Permanent equipedCreature = game.getPermanent(equipment.getAttachedTo()); for (Permanent perm : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (equipedCreature.getColor().shares(perm.getColor())) { + if (equipedCreature.getColor(game).shares(perm.getColor(game))) { perm.addPower(power.calculate(game, source, this)); perm.addToughness(toughness.calculate(game, source, this)); diff --git a/Mage.Sets/src/mage/sets/commander2013/RoughTumble.java b/Mage.Sets/src/mage/sets/commander2013/RoughTumble.java index e281caac892..14ec4d4e8be 100644 --- a/Mage.Sets/src/mage/sets/commander2013/RoughTumble.java +++ b/Mage.Sets/src/mage/sets/commander2013/RoughTumble.java @@ -55,20 +55,16 @@ public class RoughTumble extends SplitCard { super(ownerId, 118, "Rough", "Tumble", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{R}", "{5}{R}", false); this.expansionSetCode = "C13"; - this.color.setRed(true); - // Rough // Rough deals 2 damage to each creature without flying. Effect effect = new DamageAllEffect(2, filterWithoutFlying); effect.setText("Rough deals 2 damage to each creature without flying"); - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addEffect(effect); // Tumble // Tumble deals 6 damage to each creature with flying. effect = new DamageAllEffect(6, filterFlying); effect.setText("Tumble deals 6 damage to each creature with flying"); - getRightHalfCard().getColor().setRed(true); getRightHalfCard().getSpellAbility().addEffect(effect); } diff --git a/Mage.Sets/src/mage/sets/commander2014/NecromanticSelection.java b/Mage.Sets/src/mage/sets/commander2014/NecromanticSelection.java index 4877ed2151b..b595331cf91 100644 --- a/Mage.Sets/src/mage/sets/commander2014/NecromanticSelection.java +++ b/Mage.Sets/src/mage/sets/commander2014/NecromanticSelection.java @@ -168,7 +168,7 @@ class NecromanticSelectionContinuousEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - creature.getColor().setBlack(true); + creature.getColor(game).setBlack(true); } break; } diff --git a/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java b/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java index d232cccae20..1e6b0bb23a1 100644 --- a/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java +++ b/Mage.Sets/src/mage/sets/conflux/FiligreeFracture.java @@ -89,7 +89,7 @@ class FiligreeFractureEffect extends OneShotEffect { Player player = game.getPlayer(source.getControllerId()); Permanent permanent = (Permanent) game.getLastKnownInformation(source.getFirstTarget(), Zone.BATTLEFIELD); if (player != null && permanent != null - && (permanent.getColor().isBlack() || permanent.getColor().isBlue())) { + && (permanent.getColor(game).isBlack() || permanent.getColor(game).isBlue())) { player.drawCards(1, game); return true; } diff --git a/Mage.Sets/src/mage/sets/conflux/KederektParasite.java b/Mage.Sets/src/mage/sets/conflux/KederektParasite.java index bbe0731e8be..cf42f9ab4af 100644 --- a/Mage.Sets/src/mage/sets/conflux/KederektParasite.java +++ b/Mage.Sets/src/mage/sets/conflux/KederektParasite.java @@ -89,7 +89,7 @@ class KederektParasiteTriggeredAbility extends TriggeredAbilityImpl { if (event.getType() == GameEvent.EventType.DREW_CARD && game.getOpponents(this.getControllerId()).contains(event.getPlayerId())) { boolean youControlRedPermanent = false; for (Permanent permanent : game.getBattlefield().getAllActivePermanents(this.getControllerId())) { - if (permanent.getColor().isRed()) { + if (permanent.getColor(game).isRed()) { youControlRedPermanent = true; break; } diff --git a/Mage.Sets/src/mage/sets/darksteel/MycosynthLattice.java b/Mage.Sets/src/mage/sets/darksteel/MycosynthLattice.java index f45b2cc3f64..1cd753fd6d0 100644 --- a/Mage.Sets/src/mage/sets/darksteel/MycosynthLattice.java +++ b/Mage.Sets/src/mage/sets/darksteel/MycosynthLattice.java @@ -121,36 +121,36 @@ class EverythingIsColorlessEffect extends ContinuousEffectImpl { ObjectColor colorless = new ObjectColor(); // permaments for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - perm.getColor().setColor(colorless); + perm.getColor(game).setColor(colorless); } // spells for (MageObject object : game.getStack()) { if (object instanceof Spell) { - object.getColor().setColor(colorless); + object.getColor(game).setColor(colorless); } } // exile for (Card card : game.getExile().getAllCards(game)) { - card.getColor().setColor(colorless); + game.getState().getCreateCardAttribute(card).getColor().setColor(colorless); } // command for (CommandObject commandObject : game.getState().getCommand()) { - commandObject.getColor().setColor(colorless); + commandObject.getColor(game).setColor(colorless); } for (UUID playerId : controller.getInRange()) { Player player = game.getPlayer(playerId); if (player != null) { // hand for (Card card: player.getHand().getCards(game)) { - card.getColor().setColor(colorless); + game.getState().getCreateCardAttribute(card).getColor().setColor(colorless); } // library for (Card card : player.getLibrary().getCards(game)) { - card.getColor().setColor(colorless); + game.getState().getCreateCardAttribute(card).getColor().setColor(colorless); } // graveyard for (Card card : player.getGraveyard().getCards(game)) { - card.getColor().setColor(colorless); + game.getState().getCreateCardAttribute(card).getColor().setColor(colorless); } } } diff --git a/Mage.Sets/src/mage/sets/dissension/HideSeek.java b/Mage.Sets/src/mage/sets/dissension/HideSeek.java index 6d407fea200..ec7a7158069 100644 --- a/Mage.Sets/src/mage/sets/dissension/HideSeek.java +++ b/Mage.Sets/src/mage/sets/dissension/HideSeek.java @@ -65,21 +65,13 @@ public class HideSeek extends SplitCard { super(ownerId, 151, "Hide", "Seek", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{R}{W}", "{W}{B}", false); this.expansionSetCode = "DIS"; - this.color.setRed(true); - this.color.setWhite(true); - this.color.setBlack(true); - // Hide // Put target artifact or enchantment on the bottom of its owner's library. - getLeftHalfCard().getColor().setRed(true); - getLeftHalfCard().getColor().setWhite(true); getLeftHalfCard().getSpellAbility().addEffect(new PutOnLibraryTargetEffect(false)); getLeftHalfCard().getSpellAbility().addTarget(new TargetPermanent(filter)); // Seek // Search target opponent's library for a card and exile it. You gain life equal to its converted mana cost. Then that player shuffles his or her library.. - getRightHalfCard().getColor().setWhite(true); - getRightHalfCard().getColor().setBlack(true); getRightHalfCard().getSpellAbility().addEffect(new SeekEffect()); getRightHalfCard().getSpellAbility().addTarget(new TargetOpponent()); diff --git a/Mage.Sets/src/mage/sets/dissension/OddsEnds.java b/Mage.Sets/src/mage/sets/dissension/OddsEnds.java index 761f8a793df..c30478d4cc9 100644 --- a/Mage.Sets/src/mage/sets/dissension/OddsEnds.java +++ b/Mage.Sets/src/mage/sets/dissension/OddsEnds.java @@ -65,21 +65,13 @@ public class OddsEnds extends SplitCard { super(ownerId, 153, "Odds", "Ends", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{U}{R}", "{3}{R}{W}", false); this.expansionSetCode = "DIS"; - this.color.setBlue(true); - this.color.setRed(true); - this.color.setWhite(true); - // Odds // Flip a coin. If it comes up heads, counter target instant or sorcery spell. If it comes up tails, copy that spell and you may choose new targets for the copy. - getLeftHalfCard().getColor().setBlue(true); - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addEffect(new OddsEffect()); getLeftHalfCard().getSpellAbility().addTarget(new TargetSpell(filter)); // Ends // Target player sacrifices two attacking creatures. - getRightHalfCard().getColor().setRed(true); - getRightHalfCard().getColor().setWhite(true); getRightHalfCard().getSpellAbility().addEffect(new SacrificeEffect(new FilterAttackingCreature(), 2, "Target player")); getRightHalfCard().getSpellAbility().addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/sets/dissension/PillarOfTheParuns.java b/Mage.Sets/src/mage/sets/dissension/PillarOfTheParuns.java index c1be32d897e..8278800e994 100644 --- a/Mage.Sets/src/mage/sets/dissension/PillarOfTheParuns.java +++ b/Mage.Sets/src/mage/sets/dissension/PillarOfTheParuns.java @@ -96,7 +96,7 @@ class MultiColoredSpellCastManaCondition extends ManaCondition implements Condit public boolean apply(Game game, Ability source) { if (source instanceof SpellAbility) { MageObject object = game.getObject(source.getSourceId()); - if (object != null && object.getColor().getColorCount() > 1) { + if (object != null && object.getColor(game).getColorCount() > 1) { return true; } } diff --git a/Mage.Sets/src/mage/sets/dissension/ResearchDevelopment.java b/Mage.Sets/src/mage/sets/dissension/ResearchDevelopment.java index 6862047730f..353b431a555 100644 --- a/Mage.Sets/src/mage/sets/dissension/ResearchDevelopment.java +++ b/Mage.Sets/src/mage/sets/dissension/ResearchDevelopment.java @@ -57,18 +57,10 @@ public class ResearchDevelopment extends SplitCard { super(ownerId, 155, "Research", "Development", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{G}{U}", "{3}{U}{R}", false); this.expansionSetCode = "DIS"; - this.color.setGreen(true); - this.color.setBlue(true); - this.color.setRed(true); - // Choose up to four cards you own from outside the game and shuffle them into your library. - getLeftHalfCard().getColor().setGreen(true); - getLeftHalfCard().getColor().setBlue(true); getLeftHalfCard().getSpellAbility().addEffect(new ResearchEffect()); // Put a 3/1 red Elemental creature token onto the battlefield unless any opponent has you draw a card. Repeat this process two more times. - getRightHalfCard().getColor().setBlue(true); - getRightHalfCard().getColor().setRed(true); getRightHalfCard().getSpellAbility().addEffect(new DevelopmentEffect()); } diff --git a/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java b/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java index db0edc63172..8eae6bf7da0 100644 --- a/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java +++ b/Mage.Sets/src/mage/sets/dissension/SupplyDemand.java @@ -56,20 +56,12 @@ public class SupplyDemand extends SplitCard { super(ownerId, 157, "Supply", "Demand", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{W}{U}", "{X}{G}{W}", false); this.expansionSetCode = "DIS"; - this.color.setGreen(true); - this.color.setWhite(true); - this.color.setBlue(true); - // Demand // Search your library for a multicolored card, reveal it, and put it into your hand. Then shuffle your library. - getLeftHalfCard().getColor().setBlue(true); - getLeftHalfCard().getColor().setWhite(true); getLeftHalfCard().getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(1, filter), true)); // Supply // Put X 1/1 green Saproling creature tokens onto the battlefield. - getRightHalfCard().getColor().setWhite(true); - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new SaprolingToken(), new ManacostVariableValue())); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/AliveWell.java b/Mage.Sets/src/mage/sets/dragonsmaze/AliveWell.java index e7c5a1be8a6..7c327b0b424 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/AliveWell.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/AliveWell.java @@ -50,18 +50,13 @@ public class AliveWell extends SplitCard { super(ownerId, 121, "Alive", "Well", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{G}","{W}", true); this.expansionSetCode = "DGM"; - this.color.setGreen(true); - this.color.setWhite(true); - // Alive // Put a 3/3 green Centaur creature token onto the battlefield. - getLeftHalfCard().getColor().setGreen(true); getLeftHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new CentaurToken())); // Well // You gain 2 life for each creature you control. - getRightHalfCard().getColor().setWhite(true); getRightHalfCard().getSpellAbility().addEffect(new WellEffect()); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ArmedDangerous.java b/Mage.Sets/src/mage/sets/dragonsmaze/ArmedDangerous.java index b72294c0f8b..9e254100237 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ArmedDangerous.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ArmedDangerous.java @@ -51,19 +51,14 @@ public class ArmedDangerous extends SplitCard { super(ownerId, 122, "Armed", "Dangerous", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{1}{R}", "{3}{G}", true); this.expansionSetCode = "DGM"; - this.color.setRed(true); - this.color.setGreen(true); - // Armed // Target creature gets +1/+1 and gains double strike until end of turn. - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addEffect(new BoostTargetEffect(1,1, Duration.EndOfTurn)); getLeftHalfCard().getSpellAbility().addEffect(new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn)); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Dangerous // All creatures able to block target creature this turn do so. - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addEffect(new MustBeBlockedByAllTargetEffect(Duration.EndOfTurn)); getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/BeckCall.java b/Mage.Sets/src/mage/sets/dragonsmaze/BeckCall.java index 5aa0750d09b..ad25ec436b2 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/BeckCall.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/BeckCall.java @@ -55,20 +55,12 @@ public class BeckCall extends SplitCard { super(ownerId, 123, "Beck", "Call", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{G}{U}", "{4}{W}{U}", true); this.expansionSetCode = "DGM"; - this.color.setWhite(true); - this.color.setBlue(true); - this.color.setGreen(true); - // Beck // Whenever a creature enters the battlefield this turn, you may draw a card. - getLeftHalfCard().getColor().setGreen(true); - getLeftHalfCard().getColor().setBlue(true); getLeftHalfCard().getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new BeckTriggeredAbility())); // Call // Put four 1/1 white Bird creature tokens with flying onto the battlefield. - getRightHalfCard().getColor().setWhite(true); - getRightHalfCard().getColor().setBlue(true); getRightHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new BirdToken(),4)); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/DownDirty.java b/Mage.Sets/src/mage/sets/dragonsmaze/DownDirty.java index 8f6e6827ec5..9619feceec8 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/DownDirty.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/DownDirty.java @@ -49,18 +49,13 @@ public class DownDirty extends SplitCard { super(ownerId, 126, "Down", "Dirty", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{3}{B}", "{2}{G}", true); this.expansionSetCode = "DGM"; - this.color.setBlack(true); - this.color.setGreen(true); - // Down // Target player discards two cards. - getLeftHalfCard().getColor().setBlack(true); getLeftHalfCard().getSpellAbility().addEffect(new DiscardTargetEffect(2)); getLeftHalfCard().getSpellAbility().addTarget(new TargetPlayer()); // Dirty // Return target card from your graveyard to your hand. - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addEffect(new ReturnFromGraveyardToHandTargetEffect()); getRightHalfCard().getSpellAbility().addTarget(new TargetCardInYourGraveyard()); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/FarAway.java b/Mage.Sets/src/mage/sets/dragonsmaze/FarAway.java index a35e569b005..1deba123e03 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/FarAway.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/FarAway.java @@ -50,18 +50,13 @@ public class FarAway extends SplitCard { super(ownerId, 127, "Far", "Away", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{U}", "{2}{B}", true); this.expansionSetCode = "DGM"; - this.color.setBlue(true); - this.color.setBlack(true); - // Far // Return target creature to its owner's hand. - getLeftHalfCard().getColor().setBlue(true); getLeftHalfCard().getSpellAbility().addEffect(new ReturnToHandTargetEffect()); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Away // Target player sacrifices a creature. - getRightHalfCard().getColor().setBlack(true); getRightHalfCard().getSpellAbility().addEffect(new SacrificeEffect(new FilterCreaturePermanent(), 1, "Target player")); getRightHalfCard().getSpellAbility().addTarget(new TargetPlayer()); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/FleshBlood.java b/Mage.Sets/src/mage/sets/dragonsmaze/FleshBlood.java index daf7132e91e..547266d6b68 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/FleshBlood.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/FleshBlood.java @@ -60,14 +60,8 @@ public class FleshBlood extends SplitCard { super(ownerId, 128, "Flesh", "Blood", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{3}{B}{G}", "{R}{G}",true); this.expansionSetCode = "DGM"; - this.color.setBlack(true); - this.color.setGreen(true); - this.color.setRed(true); - // Flesh // Exile target creature card from a graveyard. Put X +1/+1 counters on target creature, where X is the power of the card you exiled. - getLeftHalfCard().getColor().setBlack(true); - getLeftHalfCard().getColor().setGreen(true); Target target = new TargetCardInGraveyard(new FilterCreatureCard()); getLeftHalfCard().getSpellAbility().addTarget(target); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); @@ -75,8 +69,6 @@ public class FleshBlood extends SplitCard { // Blood // Target creature you control deals damage equal to its power to target creature or player. - getRightHalfCard().getColor().setRed(true); - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); getRightHalfCard().getSpellAbility().addTarget(new TargetCreatureOrPlayer()); getRightHalfCard().getSpellAbility().addEffect(new BloodEffect()); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/GiveTake.java b/Mage.Sets/src/mage/sets/dragonsmaze/GiveTake.java index ab375aaf048..9a9ac0ba54b 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/GiveTake.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/GiveTake.java @@ -52,20 +52,15 @@ public class GiveTake extends SplitCard { super(ownerId, 129, "Give", "Take", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{G}","{2}{U}", true); this.expansionSetCode = "DGM"; - this.color.setGreen(true); - this.color.setBlue(true); - // Give // Put three +1/+1 counters on target creature. getLeftHalfCard().getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance(3))); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); - getLeftHalfCard().getColor().setGreen(true); // Take // Remove all +1/+1 counters from target creature you control. Draw that many cards. getRightHalfCard().getSpellAbility().addEffect(new TakeEffect()); getRightHalfCard().getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); - getLeftHalfCard().getColor().setBlue(true); } public GiveTake(final GiveTake card) { diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ProfitLoss.java b/Mage.Sets/src/mage/sets/dragonsmaze/ProfitLoss.java index afb564d344c..c5b6d8b250a 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ProfitLoss.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ProfitLoss.java @@ -57,17 +57,12 @@ public class ProfitLoss extends SplitCard { super(ownerId, 130, "Profit", "Loss", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{W}", "{2}{B}", true); this.expansionSetCode = "DGM"; - this.color.setWhite(true); - this.color.setBlack(true); - // Profit // Creatures you control get +1/+1 until end of turn. - getLeftHalfCard().getColor().setBlue(true); getLeftHalfCard().getSpellAbility().addEffect(new BoostControlledEffect(1,1, Duration.EndOfTurn, new FilterCreaturePermanent())); // Loss // Creatures your opponents control get -1/-1 until end of turn. - getRightHalfCard().getColor().setBlack(true); getRightHalfCard().getSpellAbility().addEffect(new BoostAllEffect(-1, -1, Duration.EndOfTurn, filter, false)); } diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ProtectServe.java b/Mage.Sets/src/mage/sets/dragonsmaze/ProtectServe.java index 78264e782c5..56db78c159b 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ProtectServe.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ProtectServe.java @@ -48,18 +48,13 @@ public class ProtectServe extends SplitCard { super(ownerId, 131, "Protect", "Serve", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{W}", "{1}{U}", true); this.expansionSetCode = "DGM"; - this.color.setWhite(true); - this.color.setBlue(true); - // Protect // Target creature gets +2/+4 until end of turn. - getLeftHalfCard().getColor().setWhite(true); getLeftHalfCard().getSpellAbility().addEffect(new BoostTargetEffect(2,4, Duration.EndOfTurn)); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Serve // Target creature gets -6/-0 until end of turn. - getRightHalfCard().getColor().setBlue(true); getRightHalfCard().getSpellAbility().addEffect(new BoostTargetEffect(-6,0, Duration.EndOfTurn)); getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ReadyWilling.java b/Mage.Sets/src/mage/sets/dragonsmaze/ReadyWilling.java index 727fdcdb378..d67e095eb89 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ReadyWilling.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ReadyWilling.java @@ -53,14 +53,8 @@ public class ReadyWilling extends SplitCard { super(ownerId, 132, "Ready", "Willing", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{1}{G}{W}", "{1}{W}{B}", true); this.expansionSetCode = "DGM"; - this.color.setGreen(true); - this.color.setWhite(true); - this.color.setBlack(true); - // Ready // Creatures you control are indestructible this turn. Untap each creature you control. - getLeftHalfCard().getColor().setGreen(true); - getLeftHalfCard().getColor().setWhite(true); Effect effect = new GainAbilityAllEffect(IndestructibleAbility.getInstance(), Duration.EndOfTurn, new FilterControlledCreaturePermanent("Creatures you controll"), false); effect.setText("Creatures you control are indestructible this turn"); getLeftHalfCard().getSpellAbility().addEffect(effect); @@ -68,8 +62,6 @@ public class ReadyWilling extends SplitCard { // Willing // Creatures you control gain deathtouch and lifelink until end of turn. - getRightHalfCard().getColor().setWhite(true); - getRightHalfCard().getColor().setBlack(true); getRightHalfCard().getSpellAbility().addEffect(new GainAbilityControlledEffect(DeathtouchAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent("Creatures")) ); effect = new GainAbilityControlledEffect(LifelinkAbility.getInstance(), Duration.EndOfTurn, new FilterCreaturePermanent("Creatures")); effect.setText("Creatures you control gain lifelink until end of turn"); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/ToilTrouble.java b/Mage.Sets/src/mage/sets/dragonsmaze/ToilTrouble.java index e9ada699a40..b6b39e004bb 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/ToilTrouble.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/ToilTrouble.java @@ -54,19 +54,14 @@ public class ToilTrouble extends SplitCard { super(ownerId, 133, "Toil", "Trouble", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{B}", "{2}{R}",true); this.expansionSetCode = "DGM"; - this.color.setBlack(true); - this.color.setRed(true); - // Toil // Target player draws two cards and loses 2 life. - getLeftHalfCard().getColor().setBlack(true); getLeftHalfCard().getSpellAbility().addTarget(new TargetPlayer()); getLeftHalfCard().getSpellAbility().addEffect(new DrawCardTargetEffect(2)); getLeftHalfCard().getSpellAbility().addEffect(new LoseLifeTargetEffect(2)); // Trouble // Trouble deals damage to target player equal to the number of cards in that player's hand. - getRightHalfCard().getColor().setRed(true); Effect effect = new DamageTargetEffect(new TargetPlayerCardsInHandCount()); effect.setText("Trouble deals damage to target player equal to the number of cards in that player's hand"); getRightHalfCard().getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/TurnBurn.java b/Mage.Sets/src/mage/sets/dragonsmaze/TurnBurn.java index 01ea29ed0ab..db568c91fc8 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/TurnBurn.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/TurnBurn.java @@ -29,11 +29,9 @@ package mage.sets.dragonsmaze; import java.util.UUID; import mage.MageInt; -import mage.ObjectColor; import mage.abilities.effects.Effect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect; -import mage.abilities.effects.common.continuous.LoseAllAbilitiesTargetEffect; import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Duration; @@ -52,12 +50,8 @@ public class TurnBurn extends SplitCard { super(ownerId, 134, "Turn", "Burn", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{U}", "{1}{R}", true); this.expansionSetCode = "DGM"; - this.color.setBlue(true); - this.color.setRed(true); - // Turn // Until end of turn, target creature loses all abilities and becomes a red Weird with base power and toughness 0/1. - getLeftHalfCard().getColor().setBlue(true); Effect effect = new BecomesCreatureTargetEffect(new WeirdToken(), true, false, Duration.EndOfTurn); effect.setText("Until end of turn, target creature loses all abilities and becomes a red Weird with base power and toughness 0/1"); getLeftHalfCard().getSpellAbility().addEffect(effect); @@ -65,7 +59,6 @@ public class TurnBurn extends SplitCard { // Burn // Burn deals 2 damage to target creature or player. - getRightHalfCard().getColor().setRed(true); effect = new DamageTargetEffect(2); effect.setText("Burn deals 2 damage to target creature or player"); getRightHalfCard().getSpellAbility().addEffect(effect); diff --git a/Mage.Sets/src/mage/sets/dragonsmaze/WearTear.java b/Mage.Sets/src/mage/sets/dragonsmaze/WearTear.java index b9f6babda76..7b68dd26a31 100644 --- a/Mage.Sets/src/mage/sets/dragonsmaze/WearTear.java +++ b/Mage.Sets/src/mage/sets/dragonsmaze/WearTear.java @@ -47,19 +47,14 @@ public class WearTear extends SplitCard { super(ownerId, 135, "Wear", "Tear", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{R}", "{W}", true); this.expansionSetCode = "DGM"; - this.color.setRed(true); - this.color.setWhite(true); - // Wear // Destroy target artifact. - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addEffect(new DestroyTargetEffect()); Target target = new TargetArtifactPermanent(); getLeftHalfCard().getSpellAbility().addTarget(target); // Tear // Destroy target enchantment. - getRightHalfCard().getColor().setWhite(true); getRightHalfCard().getSpellAbility().addEffect(new DestroyTargetEffect()); target = new TargetPermanent(new FilterEnchantment()); getRightHalfCard().getSpellAbility().addTarget(target); diff --git a/Mage.Sets/src/mage/sets/dragonsoftarkir/DisplayOfDominance.java b/Mage.Sets/src/mage/sets/dragonsoftarkir/DisplayOfDominance.java index 04b2d05bc3b..43d9ebeb73a 100644 --- a/Mage.Sets/src/mage/sets/dragonsoftarkir/DisplayOfDominance.java +++ b/Mage.Sets/src/mage/sets/dragonsoftarkir/DisplayOfDominance.java @@ -124,7 +124,7 @@ class DisplayOfDominanceEffect extends ContinuousRuleModifyingEffectImpl { MageObject mageObject = game.getObject(event.getSourceId()); if (game.getPlayer(ability.getControllerId()).hasOpponent(event.getPlayerId(), game) && mageObject instanceof Spell && - (mageObject.getColor().isBlack() || mageObject.getColor().isBlue())) { + (mageObject.getColor(game).isBlack() || mageObject.getColor(game).isBlue())) { Permanent permanent = game.getPermanent(event.getTargetId()); return permanent != null && permanent.getControllerId().equals(ability.getControllerId()); } diff --git a/Mage.Sets/src/mage/sets/eventide/BloomTender.java b/Mage.Sets/src/mage/sets/eventide/BloomTender.java index da2614e5d88..f4544ddc232 100644 --- a/Mage.Sets/src/mage/sets/eventide/BloomTender.java +++ b/Mage.Sets/src/mage/sets/eventide/BloomTender.java @@ -106,19 +106,19 @@ class BloomTenderEffect extends ManaEffect { if (controller != null) { Mana mana = new Mana(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controller.getId())) { - if (mana.getBlack() == 0 && permanent.getColor().isBlack()) { + if (mana.getBlack() == 0 && permanent.getColor(game).isBlack()) { mana.addBlack(); } - if (mana.getBlue() == 0 && permanent.getColor().isBlue()) { + if (mana.getBlue() == 0 && permanent.getColor(game).isBlue()) { mana.addBlue(); } - if (mana.getRed() == 0 && permanent.getColor().isRed()) { + if (mana.getRed() == 0 && permanent.getColor(game).isRed()) { mana.addRed(); } - if (mana.getGreen() == 0 && permanent.getColor().isGreen()) { + if (mana.getGreen() == 0 && permanent.getColor(game).isGreen()) { mana.addGreen(); } - if (mana.getWhite() == 0 && permanent.getColor().isWhite()) { + if (mana.getWhite() == 0 && permanent.getColor(game).isWhite()) { mana.addWhite(); } } diff --git a/Mage.Sets/src/mage/sets/eventide/IndigoFaerie.java b/Mage.Sets/src/mage/sets/eventide/IndigoFaerie.java index e7b6df7e5c6..ba3a0ef16ad 100644 --- a/Mage.Sets/src/mage/sets/eventide/IndigoFaerie.java +++ b/Mage.Sets/src/mage/sets/eventide/IndigoFaerie.java @@ -101,7 +101,7 @@ class BecomesBlueTargetEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { - permanent.getColor().setBlue(true); + permanent.getColor(game).setBlue(true); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/fifthedition/Hydroblast.java b/Mage.Sets/src/mage/sets/fifthedition/Hydroblast.java index fbc117f5c72..00e1a79ae3d 100644 --- a/Mage.Sets/src/mage/sets/fifthedition/Hydroblast.java +++ b/Mage.Sets/src/mage/sets/fifthedition/Hydroblast.java @@ -89,7 +89,7 @@ class HydroblastCounterEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - if (game.getStack().getSpell(source.getFirstTarget()).getColor().isRed()) { + if (game.getStack().getSpell(source.getFirstTarget()).getColor(game).isRed()) { game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game); } return true; @@ -119,7 +119,7 @@ class HydroblastDestroyEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getTargets().getFirstTarget()); - if (permanent != null && permanent.getColor().isRed()) { + if (permanent != null && permanent.getColor(game).isRed()) { permanent.destroy(source.getSourceId(), game, false); } return true; diff --git a/Mage.Sets/src/mage/sets/fifthedition/Justice.java b/Mage.Sets/src/mage/sets/fifthedition/Justice.java index ff880f4384c..ffa932141df 100644 --- a/Mage.Sets/src/mage/sets/fifthedition/Justice.java +++ b/Mage.Sets/src/mage/sets/fifthedition/Justice.java @@ -95,7 +95,7 @@ class JusticeTriggeredAbility extends TriggeredAbilityImpl { || event.getType() == GameEvent.EventType.DAMAGED_PLAYER || event.getType() == GameEvent.EventType.DAMAGED_PLANESWALKER) { MageObject sourceObject = game.getObject(event.getSourceId()); - if (sourceObject.getColor().isRed()) { + if (sourceObject.getColor(game).isRed()) { if (sourceObject instanceof Permanent && sourceObject.getCardType().contains(CardType.CREATURE) || sourceObject instanceof Spell) { this.getEffects().get(0).setValue("damageAmount", event.getAmount()); diff --git a/Mage.Sets/src/mage/sets/futuresight/DryadArbor.java b/Mage.Sets/src/mage/sets/futuresight/DryadArbor.java index 19da16a3ccf..9de739559ab 100644 --- a/Mage.Sets/src/mage/sets/futuresight/DryadArbor.java +++ b/Mage.Sets/src/mage/sets/futuresight/DryadArbor.java @@ -49,7 +49,7 @@ public class DryadArbor extends CardImpl { this.power = new MageInt(1); this.toughness = new MageInt(1); - this.getColor().setGreen(true); + this.color.setGreen(true); // (Dryad Arbor isn't a spell, it's affected by summoning sickness, and it has "{tap}: Add {G} to your mana pool.") this.addAbility(new GreenManaAbility()); diff --git a/Mage.Sets/src/mage/sets/futuresight/GlitteringWish.java b/Mage.Sets/src/mage/sets/futuresight/GlitteringWish.java index cff98bf319b..3f4ea5a174f 100644 --- a/Mage.Sets/src/mage/sets/futuresight/GlitteringWish.java +++ b/Mage.Sets/src/mage/sets/futuresight/GlitteringWish.java @@ -82,7 +82,7 @@ class GlitteringWishEffect extends OneShotEffect { @Override public boolean apply(MageObject input, Game game) { - return input.getColor().isMulticolored(); + return input.getColor(game).isMulticolored(); } @Override diff --git a/Mage.Sets/src/mage/sets/gatecrash/LazavDimirMastermind.java b/Mage.Sets/src/mage/sets/gatecrash/LazavDimirMastermind.java index e795b43c9ef..b3172590881 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/LazavDimirMastermind.java +++ b/Mage.Sets/src/mage/sets/gatecrash/LazavDimirMastermind.java @@ -120,7 +120,7 @@ class LazavDimirEffect extends ContinuousEffectImpl { } permanent.getPower().setValue(cardToCopy.getPower().getValue()); permanent.getToughness().setValue(cardToCopy.getToughness().getValue()); - permanent.getColor().setColor(cardToCopy.getColor()); + permanent.getColor(game).setColor(cardToCopy.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(cardToCopy.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage.Sets/src/mage/sets/iceage/Pyroblast.java b/Mage.Sets/src/mage/sets/iceage/Pyroblast.java index 536577b0297..04784cb21bf 100644 --- a/Mage.Sets/src/mage/sets/iceage/Pyroblast.java +++ b/Mage.Sets/src/mage/sets/iceage/Pyroblast.java @@ -89,7 +89,7 @@ class PyroblastCounterTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - if(game.getStack().getSpell(source.getFirstTarget()).getColor().isBlue()){ + if(game.getStack().getSpell(source.getFirstTarget()).getColor(game).isBlue()){ game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game); } return true; @@ -122,7 +122,7 @@ class DestroyTargetEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getTargets().getFirstTarget()); - if (permanent != null && permanent.getColor().isBlue()) { + if (permanent != null && permanent.getColor(game).isBlue()) { permanent.destroy(source.getSourceId(), game, false); } return true; diff --git a/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java b/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java index 90844048974..657f77a94c3 100644 --- a/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java +++ b/Mage.Sets/src/mage/sets/innistrad/EssenceOfTheWild.java @@ -137,7 +137,7 @@ class EssenceOfTheWildCopyEffect extends ContinuousEffectImpl { Permanent permanent = game.getPermanent(targetId); if (permanent != null) { permanent.setName(essence.getName()); - permanent.getColor().setColor(essence.getColor()); + permanent.getColor(game).setColor(essence.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(essence.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage.Sets/src/mage/sets/innistrad/GrimoireOfTheDead.java b/Mage.Sets/src/mage/sets/innistrad/GrimoireOfTheDead.java index 1e986905fd4..84e2bdea4ef 100644 --- a/Mage.Sets/src/mage/sets/innistrad/GrimoireOfTheDead.java +++ b/Mage.Sets/src/mage/sets/innistrad/GrimoireOfTheDead.java @@ -146,7 +146,7 @@ class GrimoireOfTheDeadEffect2 extends ContinuousEffectImpl { switch (layer) { case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - permanent.getColor().setBlack(true); + permanent.getColor(game).setBlack(true); } break; case TypeChangingEffects_4: diff --git a/Mage.Sets/src/mage/sets/invasion/AssaultBattery.java b/Mage.Sets/src/mage/sets/invasion/AssaultBattery.java index b658ba52cc7..7eb110ec646 100644 --- a/Mage.Sets/src/mage/sets/invasion/AssaultBattery.java +++ b/Mage.Sets/src/mage/sets/invasion/AssaultBattery.java @@ -50,12 +50,8 @@ public class AssaultBattery extends SplitCard { super(ownerId, 295, "Assault", "Battery", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{R}", "{3}{G}", false); this.expansionSetCode = "INV"; - this.color.setRed(true); - this.color.setGreen(true); - // Assault // Assault deals 2 damage to target creature or player. - getLeftHalfCard().getColor().setRed(true); Effect effect = new DamageTargetEffect(2); effect.setText("Assault deals 2 damage to target creature or player"); getLeftHalfCard().getSpellAbility().addEffect(effect); @@ -63,7 +59,6 @@ public class AssaultBattery extends SplitCard { // Battery // Put a 3/3 green Elephant creature token onto the battlefield. - getRightHalfCard().getColor().setGreen(true); getRightHalfCard().getSpellAbility().addEffect(new CreateTokenEffect(new ElephantToken())); } diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/HallOfTriumph.java b/Mage.Sets/src/mage/sets/journeyintonyx/HallOfTriumph.java index 9b93ba4a784..8e4ad8a781f 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/HallOfTriumph.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/HallOfTriumph.java @@ -138,7 +138,7 @@ class HallOfTriumphBoostControlledEffect extends ContinuousEffectImpl { ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color"); if (color != null) { for (Permanent perm: game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { - if (perm.getColor().shares(color)) { + if (perm.getColor(game).shares(color)) { perm.addPower(1); perm.addToughness(1); } diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/GhostfireBlade.java b/Mage.Sets/src/mage/sets/khansoftarkir/GhostfireBlade.java index 9d62871250a..2b01c9e92ac 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/GhostfireBlade.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/GhostfireBlade.java @@ -67,7 +67,7 @@ public class GhostfireBlade extends CardImpl { public void adjustCosts(Ability ability, Game game) { if (ability instanceof EquipAbility) { Permanent targetCreature = game.getPermanent(ability.getTargets().getFirstTarget()); - if (targetCreature != null && targetCreature.getColor().isColorless()) { + if (targetCreature != null && targetCreature.getColor(game).isColorless()) { CardUtil.reduceCost(ability, 2); } } diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/SarkhanTheDragonspeaker.java b/Mage.Sets/src/mage/sets/khansoftarkir/SarkhanTheDragonspeaker.java index f8aea6b73bc..cb84a4e3e85 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/SarkhanTheDragonspeaker.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/SarkhanTheDragonspeaker.java @@ -135,7 +135,7 @@ class SarkhanTheDragonspeakerEffect extends ContinuousEffectImpl { } break; case ColorChangingEffects_5: - permanent.getColor().setColor(ObjectColor.RED); + permanent.getColor(game).setColor(ObjectColor.RED); break; case AbilityAddingRemovingEffects_6: if (sublayer == SubLayer.NA) { diff --git a/Mage.Sets/src/mage/sets/legends/Abomination.java b/Mage.Sets/src/mage/sets/legends/Abomination.java index 8ccb34a1237..0c33a7c7818 100644 --- a/Mage.Sets/src/mage/sets/legends/Abomination.java +++ b/Mage.Sets/src/mage/sets/legends/Abomination.java @@ -95,7 +95,7 @@ class AbominationTriggeredAbility extends TriggeredAbilityImpl { Permanent blocked = game.getPermanent(event.getTargetId()); Permanent abomination = game.getPermanent(sourceId); if (blocker != null && blocker != abomination - && blocker.getColor().isWhite() + && blocker.getColor(game).isWhite() && blocked == abomination) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getSourceId())); @@ -103,14 +103,14 @@ class AbominationTriggeredAbility extends TriggeredAbilityImpl { } } if (blocker != null && blocker == abomination - && game.getPermanent(event.getTargetId()).getColor().isWhite()) { + && game.getPermanent(event.getTargetId()).getColor(game).isWhite()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); return true; } } if (blocker != null && blocker != abomination - && blocker.getColor().isGreen() + && blocker.getColor(game).isGreen() && blocked == abomination) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getSourceId())); @@ -118,7 +118,7 @@ class AbominationTriggeredAbility extends TriggeredAbilityImpl { } } if (blocker != null && blocker == abomination - && game.getPermanent(event.getTargetId()).getColor().isGreen()) { + && game.getPermanent(event.getTargetId()).getColor(game).isGreen()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); return true; diff --git a/Mage.Sets/src/mage/sets/limitedalpha/CrystalRod.java b/Mage.Sets/src/mage/sets/limitedalpha/CrystalRod.java index fb43ebc33d1..3b0b24aff4a 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/CrystalRod.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/CrystalRod.java @@ -86,7 +86,7 @@ class CrystalRodAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().contains(ObjectColor.BLUE)) { + if (spell != null && spell.getColor(game).contains(ObjectColor.BLUE)) { return true; } } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/IronStar.java b/Mage.Sets/src/mage/sets/limitedalpha/IronStar.java index 24c38dacdff..0ff69ced1f0 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/IronStar.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/IronStar.java @@ -86,7 +86,7 @@ class IronStarAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().contains(ObjectColor.RED)) { + if (spell != null && spell.getColor(game).contains(ObjectColor.RED)) { return true; } } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/IvoryCup.java b/Mage.Sets/src/mage/sets/limitedalpha/IvoryCup.java index 03ce902aa2e..cfde122dc89 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/IvoryCup.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/IvoryCup.java @@ -85,7 +85,7 @@ class IvoryCupAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().contains(ObjectColor.WHITE)) { + if (spell != null && spell.getColor(game).contains(ObjectColor.WHITE)) { return true; } } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/ThroneOfBone.java b/Mage.Sets/src/mage/sets/limitedalpha/ThroneOfBone.java index 57cb3044207..529bd128f8d 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/ThroneOfBone.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/ThroneOfBone.java @@ -86,7 +86,7 @@ class ThroneOfBoneAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().contains(ObjectColor.BLACK)) { + if (spell != null && spell.getColor(game).contains(ObjectColor.BLACK)) { return true; } } diff --git a/Mage.Sets/src/mage/sets/limitedalpha/WoodenSphere.java b/Mage.Sets/src/mage/sets/limitedalpha/WoodenSphere.java index 54c93977a9c..f342a150cc6 100644 --- a/Mage.Sets/src/mage/sets/limitedalpha/WoodenSphere.java +++ b/Mage.Sets/src/mage/sets/limitedalpha/WoodenSphere.java @@ -86,7 +86,7 @@ class WoodenSphereAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().contains(ObjectColor.GREEN)) { + if (spell != null && spell.getColor(game).contains(ObjectColor.GREEN)) { return true; } } diff --git a/Mage.Sets/src/mage/sets/limitedbeta/Fork.java b/Mage.Sets/src/mage/sets/limitedbeta/Fork.java index 4db929c4711..534f9d774c7 100644 --- a/Mage.Sets/src/mage/sets/limitedbeta/Fork.java +++ b/Mage.Sets/src/mage/sets/limitedbeta/Fork.java @@ -93,7 +93,7 @@ class ForkEffect extends OneShotEffect { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); if (spell != null) { Spell copy = spell.copySpell(); - copy.getColor().setRed(true); + copy.getColor(game).setRed(true); copy.setControllerId(controller.getId()); copy.setCopiedSpell(true); game.getStack().push(copy); diff --git a/Mage.Sets/src/mage/sets/magic2010/DreadWarlock.java b/Mage.Sets/src/mage/sets/magic2010/DreadWarlock.java index 2ee1ab461ab..4d6639a4fc5 100644 --- a/Mage.Sets/src/mage/sets/magic2010/DreadWarlock.java +++ b/Mage.Sets/src/mage/sets/magic2010/DreadWarlock.java @@ -114,7 +114,7 @@ class DreadWarlockEffect extends RestrictionEffect { @Override public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) { - if (blocker.getColor().isBlack()) { + if (blocker.getColor(game).isBlack()) { return true; } return false; diff --git a/Mage.Sets/src/mage/sets/magic2010/RiseFromTheGrave.java b/Mage.Sets/src/mage/sets/magic2010/RiseFromTheGrave.java index 5596fd7914c..7a6ca7f3494 100644 --- a/Mage.Sets/src/mage/sets/magic2010/RiseFromTheGrave.java +++ b/Mage.Sets/src/mage/sets/magic2010/RiseFromTheGrave.java @@ -98,7 +98,7 @@ class RiseFromTheGraveEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - creature.getColor().setBlack(true); + creature.getColor(game).setBlack(true); } break; } diff --git a/Mage.Sets/src/mage/sets/magic2010/VampireNocturnus.java b/Mage.Sets/src/mage/sets/magic2010/VampireNocturnus.java index 48e1bf123e7..526e6fe4405 100644 --- a/Mage.Sets/src/mage/sets/magic2010/VampireNocturnus.java +++ b/Mage.Sets/src/mage/sets/magic2010/VampireNocturnus.java @@ -126,7 +126,7 @@ class VampireNocturnusCondition implements Condition { if (player != null) { Card card = player.getLibrary().getFromTop(game); if (card != null) { - return card.getColor().isBlack(); + return card.getColor(game).isBlack(); } } return false; diff --git a/Mage.Sets/src/mage/sets/magic2011/FireServant.java b/Mage.Sets/src/mage/sets/magic2011/FireServant.java index 3ce806c9eeb..20458e7d2de 100644 --- a/Mage.Sets/src/mage/sets/magic2011/FireServant.java +++ b/Mage.Sets/src/mage/sets/magic2011/FireServant.java @@ -100,7 +100,7 @@ class FireServantEffect extends ReplacementEffectImpl { StackObject spell = game.getStack().getStackObject(event.getSourceId()); return spell != null && spell.getControllerId().equals(source.getControllerId()) && - spell.getColor().isRed() && + spell.getColor(game).isRed() && (spell.getCardType().contains(CardType.INSTANT) || spell.getCardType().contains(CardType.SORCERY)); } diff --git a/Mage.Sets/src/mage/sets/magic2012/ChandrasPhoenix.java b/Mage.Sets/src/mage/sets/magic2012/ChandrasPhoenix.java index 39fde9af860..27c7871fafe 100644 --- a/Mage.Sets/src/mage/sets/magic2012/ChandrasPhoenix.java +++ b/Mage.Sets/src/mage/sets/magic2012/ChandrasPhoenix.java @@ -91,7 +91,7 @@ class ChandrasPhoenixTriggeredAbility extends TriggeredAbilityImpl { if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) { Card c = game.getCard(event.getSourceId()); if (c != null) { - if (c.getColor().isRed() && (c.getCardType().contains(CardType.PLANESWALKER) || c.getCardType().contains(CardType.INSTANT) || c.getCardType().contains(CardType.SORCERY))) { + if (c.getColor(game).isRed() && (c.getCardType().contains(CardType.PLANESWALKER) || c.getCardType().contains(CardType.INSTANT) || c.getCardType().contains(CardType.SORCERY))) { return true; } } diff --git a/Mage.Sets/src/mage/sets/magic2014/FiendslayerPaladin.java b/Mage.Sets/src/mage/sets/magic2014/FiendslayerPaladin.java index 1f4efab0209..b20cc8df831 100644 --- a/Mage.Sets/src/mage/sets/magic2014/FiendslayerPaladin.java +++ b/Mage.Sets/src/mage/sets/magic2014/FiendslayerPaladin.java @@ -123,8 +123,8 @@ class FiendslayerPaladinEffect extends ContinuousRuleModifyingEffectImpl { Card targetCard = game.getCard(event.getTargetId()); StackObject stackObject = (StackObject) game.getStack().getStackObject(event.getSourceId()); if (targetCard != null && stackObject != null && targetCard.getId().equals(source.getSourceId())) { - if (stackObject.getColor().contains(ObjectColor.BLACK) - || stackObject.getColor().contains(ObjectColor.RED)) { + if (stackObject.getColor(game).contains(ObjectColor.BLACK) + || stackObject.getColor(game).contains(ObjectColor.RED)) { if (!stackObject.getControllerId().equals(source.getControllerId()) && stackObject.getCardType().contains(CardType.INSTANT) || stackObject.getCardType().contains(CardType.SORCERY)) { diff --git a/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java b/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java index 05e0d807513..0e6d2fa2706 100644 --- a/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java +++ b/Mage.Sets/src/mage/sets/magic2015/AvacynGuardianAngel.java @@ -127,7 +127,7 @@ class AvacynGuardianAngelPreventToCreatureEffect extends PreventionEffectImpl { && event.getTargetId().equals(getTargetPointer().getFirst(game, source))) { ChoiceColor choice = (ChoiceColor) source.getChoices().get(0); MageObject sourceObject = game.getObject(event.getSourceId()); - if (sourceObject != null && choice != null && sourceObject.getColor().shares(choice.getColor())) { + if (sourceObject != null && choice != null && sourceObject.getColor(game).shares(choice.getColor())) { return true; } } @@ -166,7 +166,7 @@ class AvacynGuardianAngelPreventToPlayerEffect extends PreventionEffectImpl { && event.getTargetId().equals(getTargetPointer().getFirst(game, source))) { ChoiceColor choice = (ChoiceColor) source.getChoices().get(0); MageObject sourceObject = game.getObject(event.getSourceId()); - if (sourceObject != null && choice != null && sourceObject.getColor().shares(choice.getColor())) { + if (sourceObject != null && choice != null && sourceObject.getColor(game).shares(choice.getColor())) { return true; } } diff --git a/Mage.Sets/src/mage/sets/magic2015/PolymorphistsJest.java b/Mage.Sets/src/mage/sets/magic2015/PolymorphistsJest.java index 44c582583e5..e48c828a987 100644 --- a/Mage.Sets/src/mage/sets/magic2015/PolymorphistsJest.java +++ b/Mage.Sets/src/mage/sets/magic2015/PolymorphistsJest.java @@ -115,12 +115,12 @@ class PolymorphistsJestEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - permanent.getColor().setBlack(false); - permanent.getColor().setGreen(false); - permanent.getColor().setBlue(false); - permanent.getColor().setWhite(false); - permanent.getColor().setBlack(false); - permanent.getColor().setColor(ObjectColor.BLUE); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setGreen(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setColor(ObjectColor.BLUE); } break; case AbilityAddingRemovingEffects_6: diff --git a/Mage.Sets/src/mage/sets/magic2015/SoulOfRavnica.java b/Mage.Sets/src/mage/sets/magic2015/SoulOfRavnica.java index f6c4344da3b..e77bbcf0fb3 100644 --- a/Mage.Sets/src/mage/sets/magic2015/SoulOfRavnica.java +++ b/Mage.Sets/src/mage/sets/magic2015/SoulOfRavnica.java @@ -103,19 +103,19 @@ class SoulOfRavnicaEffect extends OneShotEffect { if (controller != null) { HashSet colors = new HashSet<>(); for (Permanent permanent : game.getBattlefield().getAllActivePermanents(controller.getId())) { - if (permanent.getColor().isBlack()) { + if (permanent.getColor(game).isBlack()) { colors.add(ObjectColor.BLACK); } - if (permanent.getColor().isBlue()) { + if (permanent.getColor(game).isBlue()) { colors.add(ObjectColor.BLUE); } - if (permanent.getColor().isRed()) { + if (permanent.getColor(game).isRed()) { colors.add(ObjectColor.RED); } - if (permanent.getColor().isGreen()) { + if (permanent.getColor(game).isGreen()) { colors.add(ObjectColor.GREEN); } - if (permanent.getColor().isWhite()) { + if (permanent.getColor(game).isWhite()) { colors.add(ObjectColor.WHITE); } } diff --git a/Mage.Sets/src/mage/sets/mirrodin/ChromeMox.java b/Mage.Sets/src/mage/sets/mirrodin/ChromeMox.java index dab4e360d89..00e8256abb9 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/ChromeMox.java +++ b/Mage.Sets/src/mage/sets/mirrodin/ChromeMox.java @@ -151,7 +151,7 @@ class ChromeMoxManaEffect extends ManaEffect { if (imprintedCard != null) { Choice choice = new ChoiceImpl(true); choice.setMessage("Pick a mana color"); - ObjectColor color = imprintedCard.getColor(); + ObjectColor color = imprintedCard.getColor(game); if (color.isBlack()) { choice.getChoices().add("Black"); } diff --git a/Mage.Sets/src/mage/sets/modernmasters2015/KarplusanStrider.java b/Mage.Sets/src/mage/sets/modernmasters2015/KarplusanStrider.java index cedb57ab93d..81ceb5b06eb 100644 --- a/Mage.Sets/src/mage/sets/modernmasters2015/KarplusanStrider.java +++ b/Mage.Sets/src/mage/sets/modernmasters2015/KarplusanStrider.java @@ -111,7 +111,7 @@ class KarplusanStriderEffect extends ContinuousRuleModifyingEffectImpl { Permanent targettedPermanent = game.getPermanent(event.getTargetId()); Spell sourceSpell = game.getStack().getSpell(event.getSourceId()); if (targettedPermanent != null && sourceSpell != null) { - return sourceSpell.getColor().isBlue() || sourceSpell.getColor().isBlack(); + return sourceSpell.getColor(game).isBlue() || sourceSpell.getColor(game).isBlack(); } } return false; diff --git a/Mage.Sets/src/mage/sets/newphyrexia/CagedSun.java b/Mage.Sets/src/mage/sets/newphyrexia/CagedSun.java index 67e7b0fdfc7..e7fdb969e10 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/CagedSun.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/CagedSun.java @@ -111,7 +111,7 @@ class CagedSunEffect2 extends ContinuousEffectImpl { ObjectColor color = (ObjectColor) game.getState().getValue(permanent.getId() + "_color"); if (color != null) { for (Permanent perm: game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { - if (perm.getColor().contains(color)) { + if (perm.getColor(game).contains(color)) { perm.addPower(1); perm.addToughness(1); } diff --git a/Mage.Sets/src/mage/sets/ninthedition/Deathgazer.java b/Mage.Sets/src/mage/sets/ninthedition/Deathgazer.java index fa3a2db0fee..6bd0b64e1dd 100644 --- a/Mage.Sets/src/mage/sets/ninthedition/Deathgazer.java +++ b/Mage.Sets/src/mage/sets/ninthedition/Deathgazer.java @@ -87,7 +87,7 @@ class DeathgazerTriggeredAbility extends TriggeredAbilityImpl { if (event.getType() == GameEvent.EventType.BLOCKER_DECLARED) { if (event.getSourceId().equals(this.getSourceId())) { Permanent permanent = game.getPermanent(event.getTargetId()); - if(permanent != null && !permanent.getColor().isBlack()) + if(permanent != null && !permanent.getColor(game).isBlack()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getTargetId())); @@ -97,7 +97,7 @@ class DeathgazerTriggeredAbility extends TriggeredAbilityImpl { } if (event.getTargetId().equals(this.getSourceId())) { Permanent permanent = game.getPermanent(event.getSourceId()); - if(permanent != null && !permanent.getColor().isBlack()) + if(permanent != null && !permanent.getColor(game).isBlack()) { for (Effect effect : this.getEffects()) { effect.setTargetPointer(new FixedTarget(event.getSourceId())); diff --git a/Mage.Sets/src/mage/sets/odyssey/EarnestFellowship.java b/Mage.Sets/src/mage/sets/odyssey/EarnestFellowship.java index 4014969ef3f..583ed4bcfe5 100644 --- a/Mage.Sets/src/mage/sets/odyssey/EarnestFellowship.java +++ b/Mage.Sets/src/mage/sets/odyssey/EarnestFellowship.java @@ -97,9 +97,9 @@ class EarnestFellowshipEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { - if (permanent.getColor().hasColor()) { + if (permanent.getColor(game).hasColor()) { List colorPredicates = new ArrayList<>(); - for (ObjectColor color : permanent.getColor().getColors()) { + for (ObjectColor color : permanent.getColor(game).getColors()) { colorPredicates.add(new ColorPredicate(color)); } FilterCard filterColors = new FilterCard("its colors"); diff --git a/Mage.Sets/src/mage/sets/odyssey/TestamentOfFaith.java b/Mage.Sets/src/mage/sets/odyssey/TestamentOfFaith.java index 4b9149507ce..c19111128ea 100644 --- a/Mage.Sets/src/mage/sets/odyssey/TestamentOfFaith.java +++ b/Mage.Sets/src/mage/sets/odyssey/TestamentOfFaith.java @@ -127,8 +127,8 @@ class TestamentOfFaithBecomesCreatureSourceEffect extends ContinuousEffectImpl i break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - if (token.getColor().hasColor()) { - permanent.getColor().setColor(token.getColor()); + if (token.getColor(game).hasColor()) { + permanent.getColor(game).setColor(token.getColor(game)); } } break; diff --git a/Mage.Sets/src/mage/sets/planarchaos/BoomBust.java b/Mage.Sets/src/mage/sets/planarchaos/BoomBust.java index 5f9bd3bf3b3..b73609a114a 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/BoomBust.java +++ b/Mage.Sets/src/mage/sets/planarchaos/BoomBust.java @@ -60,11 +60,8 @@ public class BoomBust extends SplitCard { super(ownerId, 112, "Boom", "Bust", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{1}{R}", "{5}{R}", false); this.expansionSetCode = "PLC"; - this.color.setRed(true); - // Boom // Destroy target land you control and target land you don't control. - getLeftHalfCard().getColor().setRed(true); Effect effect = new DestroyTargetEffect(); effect.setText("Destroy target land you control and target land you don't control"); getLeftHalfCard().getSpellAbility().addEffect(effect); @@ -73,7 +70,6 @@ public class BoomBust extends SplitCard { // Bust // Destroy all lands. - getRightHalfCard().getColor().setRed(true); getRightHalfCard().getSpellAbility().addEffect(new DestroyAllEffect(new FilterLandPermanent())); } diff --git a/Mage.Sets/src/mage/sets/planarchaos/DeadGone.java b/Mage.Sets/src/mage/sets/planarchaos/DeadGone.java index ceab6f00f7c..199da64c059 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/DeadGone.java +++ b/Mage.Sets/src/mage/sets/planarchaos/DeadGone.java @@ -1,6 +1,5 @@ package mage.sets.planarchaos; -import mage.abilities.Mode; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.cards.Card; @@ -27,17 +26,14 @@ public class DeadGone extends SplitCard { public DeadGone(UUID ownerId) { super(ownerId, 113, "Dead", "Gone", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{R}", "{2}{R}", false); this.expansionSetCode = "PLC"; - this.color.setRed(true); // Dead // Dead deals 2 damage to target creature. - getLeftHalfCard().getColor().setRed(true); getLeftHalfCard().getSpellAbility().addEffect(new DeadDamageEffect()); getLeftHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent()); // Gone // Return target creature you don't control to its owner's hand. - getRightHalfCard().getColor().setRed(true); getRightHalfCard().getSpellAbility().addEffect(new ReturnToHandTargetEffect()); getRightHalfCard().getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); } diff --git a/Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.java b/Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.java index ef75978269e..09813d3f59c 100644 --- a/Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.java +++ b/Mage.Sets/src/mage/sets/planarchaos/LifeAndLimb.java @@ -116,7 +116,7 @@ class LifeAndLimbEffect extends ContinuousEffectImpl { } break; case ColorChangingEffects_5: - permanent.getColor().setColor(ObjectColor.GREEN); + permanent.getColor(game).setColor(ObjectColor.GREEN); break; case AbilityAddingRemovingEffects_6: boolean flag = false; diff --git a/Mage.Sets/src/mage/sets/planeshift/MeteorCrater.java b/Mage.Sets/src/mage/sets/planeshift/MeteorCrater.java index 813ac559688..c64976c569b 100644 --- a/Mage.Sets/src/mage/sets/planeshift/MeteorCrater.java +++ b/Mage.Sets/src/mage/sets/planeshift/MeteorCrater.java @@ -178,7 +178,7 @@ class MeteorCraterEffect extends ManaEffect { List controlledPermanents = game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game); Mana types = new Mana(); for (Permanent permanent : controlledPermanents) { - ObjectColor color = permanent.getColor(); + ObjectColor color = permanent.getColor(game); if (color.isBlack()) { types.add(Mana.BlackMana); } diff --git a/Mage.Sets/src/mage/sets/planeshift/ShiftingSky.java b/Mage.Sets/src/mage/sets/planeshift/ShiftingSky.java index c856eb587af..cf386bea42c 100644 --- a/Mage.Sets/src/mage/sets/planeshift/ShiftingSky.java +++ b/Mage.Sets/src/mage/sets/planeshift/ShiftingSky.java @@ -28,7 +28,6 @@ package mage.sets.planeshift; import java.util.UUID; -import mage.MageObject; import mage.ObjectColor; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; @@ -113,7 +112,7 @@ class ShiftingSkyEffect extends OneShotEffect { Player p = game.getPlayer(playerId); if (p != null) { for (Permanent chosen : game.getBattlefield().getAllActivePermanents(filter, playerId, game)) { - setObject(chosen, colorString); + setObject(chosen, colorString, game); } } } @@ -127,22 +126,22 @@ class ShiftingSkyEffect extends OneShotEffect { return new ShiftingSkyEffect(this); } - private void setObject(Permanent chosen, String colorString) { + private void setObject(Permanent chosen, String colorString, Game game) { switch (colorString) { case "W": - chosen.getColor().setWhite(true); + chosen.getColor(game).setWhite(true); break; case "B": - chosen.getColor().setBlack(true); + chosen.getColor(game).setBlack(true); break; case "U": - chosen.getColor().setBlue(true); + chosen.getColor(game).setBlue(true); break; case "G": - chosen.getColor().setGreen(true); + chosen.getColor(game).setGreen(true); break; case "R": - chosen.getColor().setRed(true); + chosen.getColor(game).setRed(true); break; } } diff --git a/Mage.Sets/src/mage/sets/ravnica/CrownOfConvergence.java b/Mage.Sets/src/mage/sets/ravnica/CrownOfConvergence.java index d65d95c083c..4493234abf8 100644 --- a/Mage.Sets/src/mage/sets/ravnica/CrownOfConvergence.java +++ b/Mage.Sets/src/mage/sets/ravnica/CrownOfConvergence.java @@ -104,7 +104,7 @@ class CrownOfConvergenceColorBoostEffect extends BoostAllEffect { Card topCard = you.getLibrary().getFromTop(game); if (topCard != null) { for (Permanent permanent: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (permanent.getColor().shares(topCard.getColor()) && !permanent.getColor().isColorless()) { + if (permanent.getColor(game).shares(topCard.getColor(game)) && !permanent.getColor(game).isColorless()) { permanent.addPower(power.calculate(game, source, this)); permanent.addToughness(toughness.calculate(game, source, this)); } diff --git a/Mage.Sets/src/mage/sets/ravnica/DimirDoppelganger.java b/Mage.Sets/src/mage/sets/ravnica/DimirDoppelganger.java index 9cc2cf0d14b..d1354b7a3a5 100644 --- a/Mage.Sets/src/mage/sets/ravnica/DimirDoppelganger.java +++ b/Mage.Sets/src/mage/sets/ravnica/DimirDoppelganger.java @@ -107,7 +107,7 @@ class DimirDoppelgangerEffect extends ContinuousEffectImpl { permanent.setName(cardToCopy.getName()); permanent.getPower().setValue(cardToCopy.getPower().getValue()); permanent.getToughness().setValue(cardToCopy.getToughness().getValue()); - permanent.getColor().setColor(cardToCopy.getColor()); + permanent.getColor(game).setColor(cardToCopy.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(cardToCopy.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage.Sets/src/mage/sets/ravnica/LeaveNoTrace.java b/Mage.Sets/src/mage/sets/ravnica/LeaveNoTrace.java index cf9a98f6318..ac98d00f4a2 100644 --- a/Mage.Sets/src/mage/sets/ravnica/LeaveNoTrace.java +++ b/Mage.Sets/src/mage/sets/ravnica/LeaveNoTrace.java @@ -92,10 +92,10 @@ class LeaveNoTraceEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent target = game.getPermanent(targetPointer.getFirst(game, source)); if (target != null) { - ObjectColor color = target.getColor(); + ObjectColor color = target.getColor(game); target.destroy(source.getSourceId(), game, false); for (Permanent p : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) { - if (p.getColor().shares(color)) { + if (p.getColor(game).shares(color)) { p.destroy(source.getSourceId(), game, false); } } diff --git a/Mage.Sets/src/mage/sets/ravnica/RallyTheRighteous.java b/Mage.Sets/src/mage/sets/ravnica/RallyTheRighteous.java index 077dfb18391..71ee000110e 100644 --- a/Mage.Sets/src/mage/sets/ravnica/RallyTheRighteous.java +++ b/Mage.Sets/src/mage/sets/ravnica/RallyTheRighteous.java @@ -93,10 +93,10 @@ class RallyTheRighteousUntapEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source)); if (target != null) { - ObjectColor color = target.getColor(); + ObjectColor color = target.getColor(game); target.untap(game); for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { - if (permanent.getColor().shares(color) && !permanent.getId().equals(target.getId())) { + if (permanent.getColor(game).shares(color) && !permanent.getId().equals(target.getId())) { permanent.untap(game); } } @@ -124,10 +124,10 @@ class RallyTheRighteousBoostEffect extends ContinuousEffectImpl { Permanent target = game.getPermanent(getTargetPointer().getFirst(game, source)); if (target != null) { affectedObjectList.add(new MageObjectReference(target, game)); - ObjectColor color = target.getColor(); + ObjectColor color = target.getColor(game); target.addPower(2); for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { - if (!permanent.getId().equals(target.getId()) && permanent.getColor().shares(color)) { + if (!permanent.getId().equals(target.getId()) && permanent.getColor(game).shares(color)) { affectedObjectList.add(new MageObjectReference(permanent, game)); } } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/CivicSaber.java b/Mage.Sets/src/mage/sets/returntoravnica/CivicSaber.java index d463ae79988..9274fc8cd53 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/CivicSaber.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/CivicSaber.java @@ -84,7 +84,7 @@ class CivicSaberColorCount implements DynamicValue { if (equipment != null) { Permanent permanent = game.getPermanent(equipment.getAttachedTo()); if (permanent != null) { - count = permanent.getColor().getColorCount(); + count = permanent.getColor(game).getColorCount(); } } return count; diff --git a/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java b/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java index 9ca8f30b745..c83780397b9 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/GraveBetrayal.java @@ -195,7 +195,7 @@ class GraveBetrayalContiniousEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - creature.getColor().setBlack(true); + creature.getColor(game).setBlack(true); } break; } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/LobberCrew.java b/Mage.Sets/src/mage/sets/returntoravnica/LobberCrew.java index 9d8a1a6b558..74623e0cfbe 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/LobberCrew.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/LobberCrew.java @@ -97,7 +97,7 @@ class LobberCrewTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isMulticolored() && event.getPlayerId().equals(getControllerId())) { + if (spell != null && spell.getColor(game).isMulticolored() && event.getPlayerId().equals(getControllerId())) { return true; } } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/Pyroconvergence.java b/Mage.Sets/src/mage/sets/returntoravnica/Pyroconvergence.java index cb2b6d91a03..1bbf63022c7 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/Pyroconvergence.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/Pyroconvergence.java @@ -85,7 +85,7 @@ class PyroconvergenceTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isMulticolored() && event.getPlayerId().equals(getControllerId())) { + if (spell != null && spell.getColor(game).isMulticolored() && event.getPlayerId().equals(getControllerId())) { return true; } } diff --git a/Mage.Sets/src/mage/sets/returntoravnica/TabletOfTheGuilds.java b/Mage.Sets/src/mage/sets/returntoravnica/TabletOfTheGuilds.java index 9d6141b3a98..b844e1a25c7 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/TabletOfTheGuilds.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/TabletOfTheGuilds.java @@ -138,10 +138,10 @@ class TabletOfTheGuildsGainLifeEffect extends OneShotEffect { ObjectColor color1 = new ObjectColor((String) game.getState().getValue(source.getSourceId() + "_color1")); ObjectColor color2 = new ObjectColor((String) game.getState().getValue(source.getSourceId() + "_color2")); int amount = 0; - if (spell.getColor().contains(color1)) { + if (spell.getColor(game).contains(color1)) { ++amount; } - if (spell.getColor().contains(color2)) { + if (spell.getColor(game).contains(color2)) { ++amount; } if (amount > 0) { diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/AllIsDust.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/AllIsDust.java index d1350b361ba..ab16b725378 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/AllIsDust.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/AllIsDust.java @@ -76,7 +76,7 @@ class AllIsDustEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { List permanents = game.getBattlefield().getActivePermanents(source.getControllerId(), game); for (Permanent p : permanents) { - if (!p.getColor().isColorless()) { + if (!p.getColor(game).isColorless()) { p.sacrifice(source.getSourceId(), game); } } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/CurseOfWizardry.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/CurseOfWizardry.java index d03c36b7c12..e91deb95bb9 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/CurseOfWizardry.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/CurseOfWizardry.java @@ -131,7 +131,7 @@ class CurseOfWizardryPlayerCastsSpellChosenColorTriggeredAbility extends Trigger if (chosenColor != null) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().shares(chosenColor)) { + if (spell != null && spell.getColor(game).shares(chosenColor)) { this.getEffects().get(0).setTargetPointer(new FixedTarget(event.getPlayerId())); return true; } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziTemple.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziTemple.java index d677a7c819f..07282e367fa 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziTemple.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/EldraziTemple.java @@ -96,6 +96,6 @@ class EldraziTempleCondition implements Condition { @Override public boolean apply(Game game, Ability source) { MageObject object = game.getObject(source.getSourceId()); - return object != null && object.hasSubtype("Eldrazi") && object.getColor().isColorless(); + return object != null && object.hasSubtype("Eldrazi") && object.getColor(game).isColorless(); } } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/ZulaportEnforcer.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/ZulaportEnforcer.java index effbf4a5858..a1b8a492f4b 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/ZulaportEnforcer.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/ZulaportEnforcer.java @@ -133,7 +133,7 @@ class ZulaportEnforcerEffect extends RestrictionEffect { @Override public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) { - if (blocker.getColor().isBlack()) { + if (blocker.getColor(game).isBlack()) { return true; } return false; diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/GrandArchitect.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/GrandArchitect.java index c2e167aebfc..267b4e2852a 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/GrandArchitect.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/GrandArchitect.java @@ -125,11 +125,11 @@ class GrandArchitectEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getFirstTarget()); if (permanent != null) { - permanent.getColor().setRed(false); - permanent.getColor().setWhite(false); - permanent.getColor().setGreen(false); - permanent.getColor().setBlue(true); - permanent.getColor().setBlack(false); + permanent.getColor(game).setRed(false); + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setGreen(false); + permanent.getColor(game).setBlue(true); + permanent.getColor(game).setBlack(false); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/LiegeOfTheTangle.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/LiegeOfTheTangle.java index f17f803ebe9..13089d5f07d 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/LiegeOfTheTangle.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/LiegeOfTheTangle.java @@ -145,7 +145,7 @@ class LiegeOfTheTangleEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - perm.getColor().setColor(token.getColor()); + perm.getColor(game).setColor(token.getColor(game)); } break; case PTChangingEffects_7: diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java index 07a5ef4dde3..a02bb9ccda7 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/QuicksilverGargantuan.java @@ -93,7 +93,7 @@ public class QuicksilverGargantuan extends CardImpl { Card card = game.getCard(source.getFirstTarget()); Permanent permanent = game.getPermanent(source.getSourceId()); permanent.setName(card.getName()); - permanent.getColor().setColor(card.getColor()); + permanent.getColor(game).setColor(card.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(card.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage.Sets/src/mage/sets/seventhedition/DarkestHour.java b/Mage.Sets/src/mage/sets/seventhedition/DarkestHour.java index 280425a5827..19022d75324 100644 --- a/Mage.Sets/src/mage/sets/seventhedition/DarkestHour.java +++ b/Mage.Sets/src/mage/sets/seventhedition/DarkestHour.java @@ -83,7 +83,7 @@ class DarkestHourEffect extends ContinuousEffectImpl { @Override public boolean apply(Game game, Ability source) { for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterCreaturePermanent(), source.getControllerId(), source.getSourceId(), game)) { - permanent.getColor().setColor(ObjectColor.BLACK); + permanent.getColor(game).setColor(ObjectColor.BLACK); } return true; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/ConsignToDream.java b/Mage.Sets/src/mage/sets/shadowmoor/ConsignToDream.java index 99f3493addf..5375589abe6 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/ConsignToDream.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/ConsignToDream.java @@ -90,8 +90,8 @@ class ConsignToDreamEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Permanent target = game.getPermanent(source.getFirstTarget()); if (target != null) { - if (target.getColor().contains(ObjectColor.RED) - || target.getColor().contains(ObjectColor.GREEN)) { + if (target.getColor(game).contains(ObjectColor.RED) + || target.getColor(game).contains(ObjectColor.GREEN)) { applied = target.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true); } else { applied = target.moveToZone(Zone.HAND, source.getSourceId(), game, false); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Gloomlance.java b/Mage.Sets/src/mage/sets/shadowmoor/Gloomlance.java index 38c20be6e2c..84c70998249 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Gloomlance.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Gloomlance.java @@ -90,8 +90,8 @@ class GloomlanceEffect extends OneShotEffect { Player targetController = game.getPlayer(targetCreature.getControllerId()); targetCreature.destroy(source.getSourceId(), game, false); Permanent destroyedCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); - if (destroyedCreature.getColor().isGreen() - || destroyedCreature.getColor().isWhite()) { + if (destroyedCreature.getColor(game).isGreen() + || destroyedCreature.getColor(game).isWhite()) { targetController.discard(1, false, source, game); return true; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/GloomwidowsFeast.java b/Mage.Sets/src/mage/sets/shadowmoor/GloomwidowsFeast.java index 64695d2f25b..f2b8a590bef 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/GloomwidowsFeast.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/GloomwidowsFeast.java @@ -101,8 +101,8 @@ class GloomwidowsFeastEffect extends OneShotEffect { if (targetCreature != null) { targetCreature.destroy(source.getSourceId(), game, false); Permanent destroyedCreature = game.getPermanentOrLKIBattlefield(source.getFirstTarget()); - if (destroyedCreature.getColor().isBlue() - || destroyedCreature.getColor().isBlack()) { + if (destroyedCreature.getColor(game).isBlue() + || destroyedCreature.getColor(game).isBlack()) { SpiderToken token = new SpiderToken(); token.putOntoBattlefield(1, game, source.getSourceId(), source.getControllerId()); return true; diff --git a/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java b/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java index 82b79bb4866..68e79db064f 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/IlluminatedFolio.java @@ -122,7 +122,7 @@ class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { Card chosenCard = cardsToCheck.get(entry.getKey(), game); if (chosenCard != null) { for (UUID cardToCheck : cardsToCheck) { - if (!cardToCheck.equals(chosenCard.getId()) && chosenCard.getColor().equals(game.getCard(cardToCheck).getColor())) { + if (!cardToCheck.equals(chosenCard.getId()) && chosenCard.getColor(game).equals(game.getCard(cardToCheck).getColor(game))) { newPossibleTargets.add(cardToCheck); } } @@ -131,7 +131,7 @@ class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { } else { for (UUID cardToCheck : cardsToCheck) { FilterCard colorFilter = new FilterCard(); - colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor())); + colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor(game))); if (cardsToCheck.count(colorFilter, game) > 1) { newPossibleTargets.add(cardToCheck); } @@ -150,7 +150,7 @@ class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { int possibleCards = 0; for (UUID cardToCheck : cardsToCheck) { FilterCard colorFilter = new FilterCard(); - colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor())); + colorFilter.add(new ColorPredicate(game.getCard(cardToCheck).getColor(game))); if (cardsToCheck.count(colorFilter, game) > 1) { ++possibleCards; } @@ -165,12 +165,12 @@ class TargetTwoCardsWithTheSameColorInHand extends TargetCardInHand { if (card != null) { if (targets.size() == 1) { Card card2 = game.getCard(targets.entrySet().iterator().next().getKey()); - if (card2 != null && card2.getColor().equals(card.getColor())) { + if (card2 != null && card2.getColor(game).equals(card.getColor(game))) { return true; } } else { FilterCard colorFilter = new FilterCard(); - colorFilter.add(new ColorPredicate(card.getColor())); + colorFilter.add(new ColorPredicate(card.getColor(game))); Player player = game.getPlayer(card.getOwnerId()); if (player.getHand().getCards(colorFilter, game).size() > 1) { return true; diff --git a/Mage.Sets/src/mage/sets/shadowmoor/InquisitorsSnare.java b/Mage.Sets/src/mage/sets/shadowmoor/InquisitorsSnare.java index e32dac76740..6e2d4644565 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/InquisitorsSnare.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/InquisitorsSnare.java @@ -96,8 +96,8 @@ class InquisitorsSnareEffect extends OneShotEffect { FilterCreaturePermanent filter = new FilterCreaturePermanent(); filter.add(new PermanentIdPredicate(targetCreature.getId())); game.addEffect(new PreventAllDamageByAllEffect(filter, Duration.EndOfTurn, false), source); - if (targetCreature.getColor().contains(ObjectColor.BLACK) - || targetCreature.getColor().contains(ObjectColor.RED)) { + if (targetCreature.getColor(game).contains(ObjectColor.BLACK) + || targetCreature.getColor(game).contains(ObjectColor.RED)) { return targetCreature.destroy(source.getSourceId(), game, false); } } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/LureboundScarecrow.java b/Mage.Sets/src/mage/sets/shadowmoor/LureboundScarecrow.java index 68066d70307..0ee0aabd1b4 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/LureboundScarecrow.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/LureboundScarecrow.java @@ -135,7 +135,7 @@ class LureboundScarecrowTriggeredAbility extends StateTriggeredAbility { ObjectColor color = (ObjectColor) game.getState().getValue(card.getId() + "_color"); if (color != null) { for (Permanent perm: game.getBattlefield().getAllActivePermanents(controllerId)) { - if (perm.getColor().contains(color)) + if (perm.getColor(game).contains(color)) return false; } return true; diff --git a/Mage.Sets/src/mage/sets/shadowmoor/OonaQueenOfTheFae.java b/Mage.Sets/src/mage/sets/shadowmoor/OonaQueenOfTheFae.java index 6e783d289df..8a6a73d484f 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/OonaQueenOfTheFae.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/OonaQueenOfTheFae.java @@ -112,7 +112,7 @@ class OonaQueenOfTheFaeEffect extends OneShotEffect { for(int i = 0; i < cardsToExile; i++) { Card card = opponent.getLibrary().removeFromTop(game); if (card != null) { - if (card.getColor().contains(choice.getColor())) { + if (card.getColor(game).contains(choice.getColor())) { cardsWithColor++; } card.moveToExile(null, null, source.getSourceId(), game); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/PaintersServant.java b/Mage.Sets/src/mage/sets/shadowmoor/PaintersServant.java index 18dbad17b8b..10364727e8c 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/PaintersServant.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/PaintersServant.java @@ -135,22 +135,22 @@ class PaintersServantEffect extends ContinuousEffectImpl { } String colorString = color.toString(); for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - setObjectColor(perm, colorString); + setObjectColor(perm, colorString, game); } // Stack for (MageObject object : game.getStack()) { if (object instanceof Spell) { - setObjectColor(object, colorString); + setObjectColor(object, colorString, game); } } // Exile for (Card card : game.getExile().getAllCards(game)) { - setObjectColor(card, colorString); + setCardColor(card, colorString, game); } // Command for (CommandObject commandObject : game.getState().getCommand()) { if (commandObject instanceof Commander) { - setObjectColor(commandObject, colorString); + setObjectColor(commandObject, colorString, game); } } @@ -159,15 +159,15 @@ class PaintersServantEffect extends ContinuousEffectImpl { if (player != null) { // Hand for (Card card : player.getHand().getCards(game)) { - setObjectColor(card, colorString); + setCardColor(card, colorString, game); } // Library for (Card card : player.getLibrary().getCards(game)) { - setObjectColor(card, colorString); + setCardColor(card, colorString, game); } // Graveyard for (Card card : player.getGraveyard().getCards(game)) { - setObjectColor(card, colorString); + setCardColor(card, colorString, game); } } } @@ -175,23 +175,44 @@ class PaintersServantEffect extends ContinuousEffectImpl { } return false; } - - protected static void setObjectColor(MageObject obj, String colorString) { - switch (colorString) { + + protected static void setCardColor(Card card, String colorString, Game game) { + ObjectColor color = game.getState().getCreateCardAttribute(card).getColor(); + switch (colorString) { case "W": - obj.getColor().setWhite(true); + color.setWhite(true); break; case "B": - obj.getColor().setBlack(true); + color.setBlack(true); break; case "U": - obj.getColor().setBlue(true); + color.setBlue(true); break; case "G": - obj.getColor().setGreen(true); + color.setGreen(true); break; case "R": - obj.getColor().setRed(true); + color.setRed(true); + break; + } + } + + protected static void setObjectColor(MageObject obj, String colorString, Game game) { + switch (colorString) { + case "W": + obj.getColor(game).setWhite(true); + break; + case "B": + obj.getColor(game).setBlack(true); + break; + case "U": + obj.getColor(game).setBlue(true); + break; + case "G": + obj.getColor(game).setGreen(true); + break; + case "R": + obj.getColor(game).setRed(true); break; } } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Scrapbasket.java b/Mage.Sets/src/mage/sets/shadowmoor/Scrapbasket.java index 6c14dc47e3b..c590ed08a1c 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Scrapbasket.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Scrapbasket.java @@ -92,11 +92,11 @@ class BecomesAllColorsEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); if (permanent != null) { - permanent.getColor().setBlack(true); - permanent.getColor().setBlue(true); - permanent.getColor().setRed(true); - permanent.getColor().setGreen(true); - permanent.getColor().setWhite(true); + permanent.getColor(game).setBlack(true); + permanent.getColor(game).setBlue(true); + permanent.getColor(game).setRed(true); + permanent.getColor(game).setGreen(true); + permanent.getColor(game).setWhite(true); return true; } return false; diff --git a/Mage.Sets/src/mage/sets/tempest/Grindstone.java b/Mage.Sets/src/mage/sets/tempest/Grindstone.java index aac4ee1fec2..f2b73cbca18 100644 --- a/Mage.Sets/src/mage/sets/tempest/Grindstone.java +++ b/Mage.Sets/src/mage/sets/tempest/Grindstone.java @@ -114,8 +114,8 @@ class GrindstoneEffect extends OneShotEffect { Card card1 = targetPlayer.getLibrary().removeFromTop(game); if (targetPlayer.getLibrary().size() > 0) { Card card2 = targetPlayer.getLibrary().removeFromTop(game); - if (card1.getColor().hasColor() && card2.getColor().hasColor()) { - colorShared = card1.getColor().shares(card2.getColor()); + if (card1.getColor(game).hasColor() && card2.getColor(game).hasColor()) { + colorShared = card1.getColor(game).shares(card2.getColor(game)); } } } diff --git a/Mage.Sets/src/mage/sets/tempest/LightOfDay.java b/Mage.Sets/src/mage/sets/tempest/LightOfDay.java index 8924dcf50bb..17348a8cb6d 100644 --- a/Mage.Sets/src/mage/sets/tempest/LightOfDay.java +++ b/Mage.Sets/src/mage/sets/tempest/LightOfDay.java @@ -95,7 +95,7 @@ class LightOfDayEffect extends ReplacementEffectImpl { if (permanent != null) { Player player = game.getPlayer(source.getControllerId()); if (player.getInRange().contains(permanent.getControllerId())) { - if (permanent.getColor().isBlack()) { + if (permanent.getColor(game).isBlack()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/tenthedition/AngelsFeather.java b/Mage.Sets/src/mage/sets/tenthedition/AngelsFeather.java index 88610167c3d..2d01c26e37f 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/AngelsFeather.java +++ b/Mage.Sets/src/mage/sets/tenthedition/AngelsFeather.java @@ -82,7 +82,7 @@ class AngelsFeatherAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isWhite()) { + if (spell != null && spell.getColor(game).isWhite()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java b/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java index ec8e5916f51..3fb21d7d48c 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java +++ b/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java @@ -82,7 +82,7 @@ class DragonsClawAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isRed()) { + if (spell != null && spell.getColor(game).isRed()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/tenthedition/KrakensEye.java b/Mage.Sets/src/mage/sets/tenthedition/KrakensEye.java index 9a81eacf97d..81e51ec0539 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/KrakensEye.java +++ b/Mage.Sets/src/mage/sets/tenthedition/KrakensEye.java @@ -82,7 +82,7 @@ class KrakensEyeAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isBlue()) { + if (spell != null && spell.getColor(game).isBlue()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/tenthedition/WurmsTooth.java b/Mage.Sets/src/mage/sets/tenthedition/WurmsTooth.java index d211eb06b33..31b2ff02519 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/WurmsTooth.java +++ b/Mage.Sets/src/mage/sets/tenthedition/WurmsTooth.java @@ -82,7 +82,7 @@ class WurmsToothAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isGreen()) { + if (spell != null && spell.getColor(game).isGreen()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/timeshifted/SolkanarTheSwampKing.java b/Mage.Sets/src/mage/sets/timeshifted/SolkanarTheSwampKing.java index d0d9f4bbeb3..282bb67dd00 100644 --- a/Mage.Sets/src/mage/sets/timeshifted/SolkanarTheSwampKing.java +++ b/Mage.Sets/src/mage/sets/timeshifted/SolkanarTheSwampKing.java @@ -91,7 +91,7 @@ class SolkanarTheSwampKingAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isBlack()) { + if (spell != null && spell.getColor(game).isBlack()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/timespiral/AncestralVision.java b/Mage.Sets/src/mage/sets/timespiral/AncestralVision.java index f3ef3a34260..08a51389a09 100644 --- a/Mage.Sets/src/mage/sets/timespiral/AncestralVision.java +++ b/Mage.Sets/src/mage/sets/timespiral/AncestralVision.java @@ -46,7 +46,7 @@ public class AncestralVision extends CardImpl { super(ownerId, 48, "Ancestral Vision", Rarity.RARE, new CardType[]{CardType.SORCERY}, ""); this.expansionSetCode = "TSP"; - this.getColor().setBlue(true); + this.color.setBlue(true); // Suspend 4-{U} this.addAbility(new SuspendAbility(4, new ManaCostsImpl("U"), this)); diff --git a/Mage.Sets/src/mage/sets/timespiral/GauntletOfPower.java b/Mage.Sets/src/mage/sets/timespiral/GauntletOfPower.java index fec94eac07e..9cbcd3812c5 100644 --- a/Mage.Sets/src/mage/sets/timespiral/GauntletOfPower.java +++ b/Mage.Sets/src/mage/sets/timespiral/GauntletOfPower.java @@ -116,7 +116,7 @@ class GauntletOfPowerEffect1 extends ContinuousEffectImpl { ObjectColor color = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color"); if (color != null) { for (Permanent perm: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { - if (perm.getColor().contains(color)) { + if (perm.getColor(game).contains(color)) { perm.addPower(1); perm.addToughness(1); } diff --git a/Mage.Sets/src/mage/sets/timespiral/GhostflameSliver.java b/Mage.Sets/src/mage/sets/timespiral/GhostflameSliver.java index 7b1e4be27d7..ffbf3229a90 100644 --- a/Mage.Sets/src/mage/sets/timespiral/GhostflameSliver.java +++ b/Mage.Sets/src/mage/sets/timespiral/GhostflameSliver.java @@ -87,7 +87,7 @@ class GhostflameSliverEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { if (filter.match(perm, game)) { - perm.getColor().setColor(colorless); + perm.getColor(game).setColor(colorless); } } return true; diff --git a/Mage.Sets/src/mage/sets/urzaslegacy/ThranLens.java b/Mage.Sets/src/mage/sets/urzaslegacy/ThranLens.java index 254189c4f63..7dfb1ef2d11 100644 --- a/Mage.Sets/src/mage/sets/urzaslegacy/ThranLens.java +++ b/Mage.Sets/src/mage/sets/urzaslegacy/ThranLens.java @@ -74,7 +74,7 @@ public class ThranLens extends CardImpl { @Override public boolean apply(Game game, Ability source) { for (Permanent perm: game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { - perm.getColor().setColor(colorless); + perm.getColor(game).setColor(colorless); } return true; } diff --git a/Mage.Sets/src/mage/sets/urzassaga/Bereavement.java b/Mage.Sets/src/mage/sets/urzassaga/Bereavement.java index 780d76549e3..84c1d1371e4 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Bereavement.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Bereavement.java @@ -84,7 +84,7 @@ class BereavementTriggeredAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.ZONE_CHANGE && ((ZoneChangeEvent)event).isDiesEvent()) { Permanent permanent = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD); - if (permanent != null && permanent.getCardType().contains(CardType.CREATURE) && permanent.getColor().isGreen()) { + if (permanent != null && permanent.getCardType().contains(CardType.CREATURE) && permanent.getColor(game).isGreen()) { this.getEffects().get(0).setTargetPointer(new FixedTarget(permanent.getControllerId())); return true; } diff --git a/Mage.Sets/src/mage/sets/urzassaga/Persecute.java b/Mage.Sets/src/mage/sets/urzassaga/Persecute.java index dccaf70e7d6..5c83ad2eeaf 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/Persecute.java +++ b/Mage.Sets/src/mage/sets/urzassaga/Persecute.java @@ -103,7 +103,7 @@ class PersecuteEffect extends OneShotEffect { controller.revealCards("Persecute", hand, game); Set cards = hand.getCards(game); for (Card card : cards) { - if (card != null && card.getColor().shares(choice.getColor())) { + if (card != null && card.getColor(game).shares(choice.getColor())) { controller.discard(card, source, game); } } diff --git a/Mage.Sets/src/mage/sets/urzassaga/TitaniasChosen.java b/Mage.Sets/src/mage/sets/urzassaga/TitaniasChosen.java index ccb6947cae5..98192bb6e63 100644 --- a/Mage.Sets/src/mage/sets/urzassaga/TitaniasChosen.java +++ b/Mage.Sets/src/mage/sets/urzassaga/TitaniasChosen.java @@ -91,7 +91,7 @@ class TitaniasChosenAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isGreen()) { + if (spell != null && spell.getColor(game).isGreen()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/vintagemasters/VolrathsShapeshifter.java b/Mage.Sets/src/mage/sets/vintagemasters/VolrathsShapeshifter.java index cd9b949838f..98aeac7b4a3 100644 --- a/Mage.Sets/src/mage/sets/vintagemasters/VolrathsShapeshifter.java +++ b/Mage.Sets/src/mage/sets/vintagemasters/VolrathsShapeshifter.java @@ -105,7 +105,7 @@ class VolrathsShapeshifterEffect extends ContinuousEffectImpl { permanent.getPower().setValue(card.getPower().getValue()); permanent.getToughness().setValue(card.getToughness().getValue()); - permanent.getColor().setColor(card.getColor()); + permanent.getColor(game).setColor(card.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(card.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage.Sets/src/mage/sets/visions/ElephantGrass.java b/Mage.Sets/src/mage/sets/visions/ElephantGrass.java index 11f14fe5eb5..bbbe371753c 100644 --- a/Mage.Sets/src/mage/sets/visions/ElephantGrass.java +++ b/Mage.Sets/src/mage/sets/visions/ElephantGrass.java @@ -90,7 +90,7 @@ class ElephantGrassReplacementEffect extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getTargetId().equals(source.getControllerId()) ) { Permanent creature = game.getPermanent(event.getSourceId()); - if(creature != null && creature.getColor().isBlack()){ + if(creature != null && creature.getColor(game).isBlack()){ return true; } } @@ -145,7 +145,7 @@ class ElephantGrassReplacementEffect2 extends ReplacementEffectImpl { public boolean applies(GameEvent event, Ability source, Game game) { if (event.getTargetId().equals(source.getControllerId()) ) { Permanent creature = game.getPermanent(event.getSourceId()); - if (creature != null && !creature.getColor().isBlack()) { + if (creature != null && !creature.getColor(game).isBlack()) { Player attackedPlayer = game.getPlayer(event.getTargetId()); if (attackedPlayer != null) { // only if a player is attacked. Attacking a planeswalker is free diff --git a/Mage.Sets/src/mage/sets/worldwake/KorFirewalker.java b/Mage.Sets/src/mage/sets/worldwake/KorFirewalker.java index 3e9552fac4a..9e46e536a43 100644 --- a/Mage.Sets/src/mage/sets/worldwake/KorFirewalker.java +++ b/Mage.Sets/src/mage/sets/worldwake/KorFirewalker.java @@ -100,7 +100,7 @@ class KorFirewalkerAbility extends TriggeredAbilityImpl { public boolean checkTrigger(GameEvent event, Game game) { if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor().isRed()) { + if (spell != null && spell.getColor(game).isRed()) { return true; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java b/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java index 27d3d4f059e..0dd3fc178fd 100644 --- a/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/PermafrostTrap.java @@ -102,7 +102,7 @@ class PermafrostTrapWatcher extends Watcher { } if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) { Permanent perm = game.getPermanent(event.getTargetId()); - if (perm.getCardType().contains(CardType.CREATURE) && perm.getColor().contains(ObjectColor.GREEN) && !perm.getControllerId().equals(controllerId)) { + if (perm.getCardType().contains(CardType.CREATURE) && perm.getColor(game).contains(ObjectColor.GREEN) && !perm.getControllerId().equals(controllerId)) { condition = true; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java b/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java index 8064807857f..27641883e14 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java @@ -106,7 +106,7 @@ class RefractionTrapWatcher extends Watcher { if (event.getType() == GameEvent.EventType.SPELL_CAST && game.getOpponents(controllerId).contains(event.getPlayerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell.getColor().isRed()) { + if (spell.getColor(game).isRed()) { if (spell.getCardType().contains(CardType.INSTANT) || spell.getCardType().contains(CardType.SORCERY)) { condition = true; diff --git a/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java b/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java index 573cb143468..f831a87dc80 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/RicochetTrap.java @@ -110,7 +110,7 @@ class RicochetTrapWatcher extends Watcher { if (event.getType() == EventType.SPELL_CAST && game.getOpponents(controllerId).contains(event.getPlayerId())) { Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell.getColor().isBlue()) { + if (spell.getColor(game).isBlue()) { condition = true; } } diff --git a/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java b/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java index c91558c586a..6d3fa2bceeb 100644 --- a/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/SlingbowTrap.java @@ -101,7 +101,7 @@ class SlingbowTrapAlternativeCost extends AlternativeCostImpl { List attackers = game.getCombat().getAttackers(); for (UUID creatureId : attackers) { Permanent creature = game.getPermanent(creatureId); - if (creature.getColor().isBlack() + if (creature.getColor(game).isBlack() && creature.getAbilities().contains(FlyingAbility.getInstance())) { return true; } diff --git a/Mage.Sets/src/mage/sets/zendikar/ChandraAblaze.java b/Mage.Sets/src/mage/sets/zendikar/ChandraAblaze.java index fb8c03d134b..e7451fd3a29 100644 --- a/Mage.Sets/src/mage/sets/zendikar/ChandraAblaze.java +++ b/Mage.Sets/src/mage/sets/zendikar/ChandraAblaze.java @@ -145,7 +145,7 @@ class ChandraAblazeEffect2 extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Card card = (Card) this.getValue("discardedCard"); - if (card != null && card.getColor().isRed()) { + if (card != null && card.getColor(game).isRed()) { Permanent permanent = game.getPermanent(targetPointer.getFirst(game, source)); if (permanent != null) { permanent.damage(4, source.getSourceId(), game, false, true); diff --git a/Mage.Sets/src/mage/sets/zendikar/IonaShieldOfEmeria.java b/Mage.Sets/src/mage/sets/zendikar/IonaShieldOfEmeria.java index 8bfef2890eb..6bf1a7ce352 100644 --- a/Mage.Sets/src/mage/sets/zendikar/IonaShieldOfEmeria.java +++ b/Mage.Sets/src/mage/sets/zendikar/IonaShieldOfEmeria.java @@ -151,7 +151,7 @@ class IonaShieldOfEmeriaReplacementEffect extends ContinuousRuleModifyingEffectI if (game.getOpponents(source.getControllerId()).contains(event.getPlayerId()) ) { ObjectColor chosenColor = (ObjectColor) game.getState().getValue(source.getSourceId() + "_color"); Card card = game.getCard(event.getSourceId()); - if (chosenColor != null && card != null && card.getColor().contains(chosenColor)) { + if (chosenColor != null && card != null && card.getColor(game).contains(chosenColor)) { return true; } } diff --git a/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastCreaturesTest.java b/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastCreaturesTest.java index f7b3649f421..f65e7655177 100644 --- a/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastCreaturesTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/AI/basic/CastCreaturesTest.java @@ -29,6 +29,7 @@ package org.mage.test.AI.basic; import mage.constants.PhaseStep; import mage.constants.Zone; +import org.junit.Ignore; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBaseAI; @@ -51,4 +52,33 @@ public class CastCreaturesTest extends CardTestPlayerBaseAI { assertPermanentCount(playerA, "Silvercoat Lion", 1); } + + @Test + public void testSimpleCast2() { + addCard(Zone.HAND, playerA, "Silvercoat Lion"); + addCard(Zone.HAND, playerA, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Silvercoat Lion", 2); + } + + @Test + @Ignore // AI should cast Myr Enforcer -> Check why it does not + public void testSimpleCast3() { + // Affinity for artifacts (This spell costs less to cast for each artifact you control.) + addCard(Zone.HAND, playerA, "Myr Enforcer"); + // {T}: Add to your mana pool. + // {T}, {1}, Sacrifice Mind Stone: Draw a card. + addCard(Zone.BATTLEFIELD, playerA, "Mind Stone", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Myr Enforcer", 1); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java new file mode 100644 index 00000000000..9ad87ebb8c5 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java @@ -0,0 +1,157 @@ +/* + * 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.cards.continuous; + +import mage.cards.Card; +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.game.permanent.Permanent; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class PaintersServantTest extends CardTestPlayerBase { + + /** + * Test that the added color is applied as Painter's Servant is on the battlefield + */ + @Test + public void testColorSet() { + // As Painter's Servant enters the battlefield, choose a color. + // All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors. + addCard(Zone.HAND, playerA, "Painter's Servant", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + + addCard(Zone.HAND, playerB, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); + setChoice(playerA, "Blue"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Painter's Servant", 1); + + Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue()); + + silvercoatLion = getPermanent("Silvercoat Lion", playerB); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue()); + + for(Card card: playerA.getHand().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isRed()); + Assert.assertEquals(true, card.getColor(currentGame).isBlue()); + } + for(Card card: playerB.getHand().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isRed()); + Assert.assertEquals(true, card.getColor(currentGame).isBlue()); + } + for(Card card: playerA.getGraveyard().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isWhite()); + Assert.assertEquals(true, card.getColor(currentGame).isBlue()); + } + for(Card card: playerB.getGraveyard().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isWhite()); + Assert.assertEquals(true, card.getColor(currentGame).isBlue()); + } + + } + + /** + * Test that the added color is no longer applied as Painter's Servant has left the battlefield + */ + @Test + public void testColorReset() { + // As Painter's Servant enters the battlefield, choose a color. + // All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors. + addCard(Zone.HAND, playerA, "Painter's Servant", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); + + addCard(Zone.HAND, playerB, "Lightning Bolt",2); + addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); + addCard(Zone.GRAVEYARD, playerB, "Silvercoat Lion"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); + setChoice(playerA, "Blue"); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Lightning Bolt", "Painter's Servant"); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerA, "Painter's Servant", 1); + + Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isBlue()); + + silvercoatLion = getPermanent("Silvercoat Lion", playerB); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isBlue()); + + for(Card card: playerA.getHand().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isRed()); + Assert.assertEquals(false, card.getColor(currentGame).isBlue()); + } + for(Card card: playerB.getHand().getCards(currentGame)) { + Assert.assertEquals(true, card.getColor(currentGame).isRed()); + Assert.assertEquals(false, card.getColor(currentGame).isBlue()); + } + for(Card card: playerA.getGraveyard().getCards(currentGame)) { + if(card.getName().equals("Silvercoat Lion")) { + Assert.assertEquals(true, card.getColor(currentGame).isWhite()); + Assert.assertEquals(false, card.getColor(currentGame).isBlue()); + } + } + for(Card card: playerB.getGraveyard().getCards(currentGame)) { + if(card.getName().equals("Silvercoat Lion")) { + Assert.assertEquals(true, card.getColor(currentGame).isWhite()); + Assert.assertEquals(false, card.getColor(currentGame).isBlue()); + } + } + + } + +} diff --git a/Mage/src/mage/MageObject.java b/Mage/src/mage/MageObject.java index 0694e0dfd7f..c2bd51447c8 100644 --- a/Mage/src/mage/MageObject.java +++ b/Mage/src/mage/MageObject.java @@ -26,7 +26,7 @@ public interface MageObject extends MageItem, Serializable { Abilities getAbilities(); boolean hasAbility(UUID abilityId, Game game); - ObjectColor getColor(); + ObjectColor getColor(Game game); ManaCosts getManaCost(); MageInt getPower(); diff --git a/Mage/src/mage/MageObjectImpl.java b/Mage/src/mage/MageObjectImpl.java index 075dfe1dc62..1d0c6400bcb 100644 --- a/Mage/src/mage/MageObjectImpl.java +++ b/Mage/src/mage/MageObjectImpl.java @@ -157,7 +157,7 @@ public abstract class MageObjectImpl implements MageObject { } @Override - public ObjectColor getColor() { + public ObjectColor getColor(Game game) { return color; } diff --git a/Mage/src/mage/abilities/effects/common/CopyEffect.java b/Mage/src/mage/abilities/effects/common/CopyEffect.java index c9eeb76f353..bbb1d6ba0ba 100644 --- a/Mage/src/mage/abilities/effects/common/CopyEffect.java +++ b/Mage/src/mage/abilities/effects/common/CopyEffect.java @@ -97,7 +97,7 @@ public class CopyEffect extends ContinuousEffectImpl { return false; } permanent.setName(target.getName()); - permanent.getColor().setColor(target.getColor()); + permanent.getColor(game).setColor(target.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(target.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage/src/mage/abilities/effects/common/CopyTokenEffect.java b/Mage/src/mage/abilities/effects/common/CopyTokenEffect.java index 24b30457dda..c614624f4b9 100644 --- a/Mage/src/mage/abilities/effects/common/CopyTokenEffect.java +++ b/Mage/src/mage/abilities/effects/common/CopyTokenEffect.java @@ -29,7 +29,7 @@ public class CopyTokenEffect extends ContinuousEffectImpl { public boolean apply(Game game, Ability source) { Permanent permanent = game.getPermanent(source.getSourceId()); permanent.setName(token.getName()); - permanent.getColor().setColor(token.getColor()); + permanent.getColor(game).setColor(token.getColor(game)); permanent.getCardType().clear(); for (CardType type: token.getCardType()) { permanent.getCardType().add(type); diff --git a/Mage/src/mage/abilities/effects/common/continuous/AddCardColorAttachedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/AddCardColorAttachedEffect.java index 93e1b523160..b81b21de6f6 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/AddCardColorAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/AddCardColorAttachedEffect.java @@ -63,15 +63,15 @@ public class AddCardColorAttachedEffect extends ContinuousEffectImpl { Permanent target = game.getPermanent(equipment.getAttachedTo()); if (target != null) { if (addedColor.isBlack()) - target.getColor().setBlack(true); + target.getColor(game).setBlack(true); if (addedColor.isBlue()) - target.getColor().setBlue(true); + target.getColor(game).setBlue(true); if (addedColor.isWhite()) - target.getColor().setWhite(true); + target.getColor(game).setWhite(true); if (addedColor.isGreen()) - target.getColor().setGreen(true); + target.getColor(game).setGreen(true); if (addedColor.isRed()) - target.getColor().setRed(true); + target.getColor(game).setRed(true); } } return true; diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java index 27c18a29b57..068fee171e2 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesBasicLandEnchantedEffect.java @@ -85,11 +85,11 @@ public class BecomesBasicLandEnchantedEffect extends ContinuousEffectImpl { if (permanent != null) { switch (layer) { case ColorChangingEffects_5: - permanent.getColor().setWhite(false); - permanent.getColor().setGreen(false); - permanent.getColor().setBlack(false); - permanent.getColor().setBlue(false); - permanent.getColor().setRed(false); + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setGreen(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setRed(false); break; case AbilityAddingRemovingEffects_6: permanent.removeAllAbilities(source.getSourceId(), game); diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java index 6c74ef7e58c..b93a7cfff53 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java @@ -107,7 +107,7 @@ public class BecomesColorTargetEffect extends ContinuousEffectImpl { MageObject o = game.getObject(targetId); if (o != null) { if (o instanceof Permanent || o instanceof StackObject) { - o.getColor().setColor(objectColor); + o.getColor(game).setColor(objectColor); result = true; } } diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java index 50802b7894e..f891657cff5 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAllEffect.java @@ -93,8 +93,8 @@ public class BecomesCreatureAllEffect extends ContinuousEffectImpl { break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - if (token.getColor().hasColor()) - permanent.getColor().setColor(token.getColor()); + if (token.getColor(game).hasColor()) + permanent.getColor(game).setColor(token.getColor(game)); } break; case AbilityAddingRemovingEffects_6: diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java index 89f97a867d5..329ed1d78e4 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureAttachedEffect.java @@ -115,14 +115,14 @@ public class BecomesCreatureAttachedEffect extends ContinuousEffectImpl { case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { if (loseOther) { - permanent.getColor().setBlack(false); - permanent.getColor().setGreen(false); - permanent.getColor().setBlue(false); - permanent.getColor().setWhite(false); - permanent.getColor().setRed(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setGreen(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setRed(false); } - if (token.getColor().hasColor()) { - permanent.getColor().setColor(token.getColor()); + if (token.getColor(game).hasColor()) { + permanent.getColor(game).setColor(token.getColor(game)); } } break; diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java index ee60cdf57eb..927f8974466 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureSourceEffect.java @@ -105,8 +105,8 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl implements break; case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { - if (token.getColor().hasColor()) { - permanent.getColor().setColor(token.getColor()); + if (token.getColor(game).hasColor()) { + permanent.getColor(game).setColor(token.getColor(game)); } } break; diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java index 8bee7f94b8f..10c22909b9d 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesCreatureTargetEffect.java @@ -111,14 +111,14 @@ public class BecomesCreatureTargetEffect extends ContinuousEffectImpl { case ColorChangingEffects_5: if (sublayer == SubLayer.NA) { if (loseAllAbilities) { - permanent.getColor().setBlack(false); - permanent.getColor().setGreen(false); - permanent.getColor().setBlue(false); - permanent.getColor().setWhite(false); - permanent.getColor().setBlack(false); + permanent.getColor(game).setBlack(false); + permanent.getColor(game).setGreen(false); + permanent.getColor(game).setBlue(false); + permanent.getColor(game).setWhite(false); + permanent.getColor(game).setBlack(false); } - if (token.getColor().hasColor()) { - permanent.getColor().setColor(token.getColor()); + if (token.getColor(game).hasColor()) { + permanent.getColor(game).setColor(token.getColor(game)); } } break; diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java index 3896cd97f6e..3abed713076 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureAllEffect.java @@ -118,7 +118,7 @@ public class BecomesFaceDownCreatureAllEffect extends ContinuousEffectImpl imple permanent.getManaCost().clear(); break; case ColorChangingEffects_5: - permanent.getColor().setColor(new ObjectColor()); + permanent.getColor(game).setColor(new ObjectColor()); break; case AbilityAddingRemovingEffects_6: Card card = game.getCard(permanent.getId()); // diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java index 14bb52a456b..cd0831f27df 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesFaceDownCreatureEffect.java @@ -175,7 +175,7 @@ public class BecomesFaceDownCreatureEffect extends ContinuousEffectImpl implemen permanent.getSubtype().clear(); break; case ColorChangingEffects_5: - permanent.getColor().setColor(new ObjectColor()); + permanent.getColor(game).setColor(new ObjectColor()); break; case AbilityAddingRemovingEffects_6: Card card = game.getCard(permanent.getId()); // diff --git a/Mage/src/mage/abilities/effects/common/continuous/SetCardColorAttachedEffect.java b/Mage/src/mage/abilities/effects/common/continuous/SetCardColorAttachedEffect.java index 9cdf7026418..dd32ebda2fb 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/SetCardColorAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/SetCardColorAttachedEffect.java @@ -61,7 +61,7 @@ public class SetCardColorAttachedEffect extends ContinuousEffectImpl { if (equipment != null && equipment.getAttachedTo() != null) { Permanent target = game.getPermanent(equipment.getAttachedTo()); if (target != null) { - target.getColor().setColor(setColor); + target.getColor(game).setColor(setColor); return true; } } diff --git a/Mage/src/mage/abilities/effects/common/continuous/SetCardColorSourceEffect.java b/Mage/src/mage/abilities/effects/common/continuous/SetCardColorSourceEffect.java index 85340707a5b..e45ed9f9a42 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/SetCardColorSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/SetCardColorSourceEffect.java @@ -75,7 +75,7 @@ public class SetCardColorSourceEffect extends ContinuousEffectImpl { MageObject o = game.getObject(source.getSourceId()); if (o != null) { if (o instanceof Permanent || o instanceof StackObject) { - o.getColor().setColor(setColor); + o.getColor(game).setColor(setColor); } } diff --git a/Mage/src/mage/abilities/keyword/ConvokeAbility.java b/Mage/src/mage/abilities/keyword/ConvokeAbility.java index 9449efcb458..ef920d57d42 100644 --- a/Mage/src/mage/abilities/keyword/ConvokeAbility.java +++ b/Mage/src/mage/abilities/keyword/ConvokeAbility.java @@ -207,7 +207,7 @@ class ConvokeEffect extends OneShotEffect { String manaName; if (!perm.isTapped() && perm.tap(game)) { ManaPool manaPool = controller.getManaPool(); - Choice chooseManaType = buildChoice(perm.getColor(), unpaid.getMana()); + Choice chooseManaType = buildChoice(perm.getColor(game), unpaid.getMana()); if (chooseManaType.getChoices().size() > 0) { if (chooseManaType.getChoices().size() > 1) { chooseManaType.getChoices().add("Colorless"); diff --git a/Mage/src/mage/abilities/keyword/FearAbility.java b/Mage/src/mage/abilities/keyword/FearAbility.java index a860cfd25aa..9324251eb86 100644 --- a/Mage/src/mage/abilities/keyword/FearAbility.java +++ b/Mage/src/mage/abilities/keyword/FearAbility.java @@ -91,7 +91,7 @@ class FearEffect extends RestrictionEffect implements MageSingleton { @Override public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) { - if (blocker.getCardType().contains(CardType.ARTIFACT) || blocker.getColor().isBlack()) { + if (blocker.getCardType().contains(CardType.ARTIFACT) || blocker.getColor(game).isBlack()) { return true; } return false; diff --git a/Mage/src/mage/abilities/keyword/IntimidateAbility.java b/Mage/src/mage/abilities/keyword/IntimidateAbility.java index c48b166f428..01e8b02bc56 100644 --- a/Mage/src/mage/abilities/keyword/IntimidateAbility.java +++ b/Mage/src/mage/abilities/keyword/IntimidateAbility.java @@ -64,7 +64,7 @@ class IntimidateEffect extends RestrictionEffect implements MageSingleton { if (blocker.getCardType().contains(CardType.ARTIFACT) && (blocker.getCardType().contains(CardType.CREATURE))) { result = true; } - if (attacker.getColor().shares(blocker.getColor())) { + if (attacker.getColor(game).shares(blocker.getColor(game))) { result = true; } return result; diff --git a/Mage/src/mage/abilities/keyword/MorphAbility.java b/Mage/src/mage/abilities/keyword/MorphAbility.java index d6ec2347383..362be6c6191 100644 --- a/Mage/src/mage/abilities/keyword/MorphAbility.java +++ b/Mage/src/mage/abilities/keyword/MorphAbility.java @@ -219,7 +219,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost } } // change spell colors - ObjectColor spellColor = spell.getColor(); + ObjectColor spellColor = spell.getColor(game); spellColor.setBlack(false); spellColor.setRed(false); spellColor.setGreen(false); @@ -297,7 +297,7 @@ public class MorphAbility extends StaticAbility implements AlternativeSourceCost mageObject.getPower().initValue(2); mageObject.getToughness().initValue(2); mageObject.getAbilities().clear(); - mageObject.getColor().setColor(new ObjectColor()); + mageObject.getColor(null).setColor(new ObjectColor()); mageObject.setName(""); mageObject.getCardType().clear(); mageObject.getCardType().add(CardType.CREATURE); diff --git a/Mage/src/mage/abilities/keyword/TransformAbility.java b/Mage/src/mage/abilities/keyword/TransformAbility.java index 55a97861b70..9b01d319c02 100644 --- a/Mage/src/mage/abilities/keyword/TransformAbility.java +++ b/Mage/src/mage/abilities/keyword/TransformAbility.java @@ -69,7 +69,7 @@ public class TransformAbility extends SimpleStaticAbility { } permanent.setName(sourceCard.getName()); - permanent.getColor().setColor(sourceCard.getColor()); + permanent.getColor(game).setColor(sourceCard.getColor(game)); permanent.getManaCost().clear(); permanent.getManaCost().add(sourceCard.getManaCost()); permanent.getCardType().clear(); diff --git a/Mage/src/mage/cards/Card.java b/Mage/src/mage/cards/Card.java index f89865d773b..91217b0c02a 100644 --- a/Mage/src/mage/cards/Card.java +++ b/Mage/src/mage/cards/Card.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.UUID; import mage.MageObject; import mage.Mana; +import mage.ObjectColor; import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.SpellAbility; @@ -134,5 +135,5 @@ public interface Card extends MageObject { */ Card getMainCard(); - void setZone(Zone zone, Game game); + void setZone(Zone zone, Game game); } diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index 9458a22fe17..b656ae3bf29 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -36,6 +36,7 @@ import java.util.UUID; import mage.MageObject; import mage.MageObjectImpl; import mage.Mana; +import mage.ObjectColor; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; @@ -59,6 +60,7 @@ import static mage.constants.Zone.PICK; import static mage.constants.Zone.STACK; import mage.counters.Counter; import mage.counters.Counters; +import mage.game.CardAttribute; import mage.game.CardState; import mage.game.Game; import mage.game.command.Commander; @@ -763,5 +765,15 @@ public abstract class CardImpl extends MageObjectImpl implements Card { public void setSpellAbility(SpellAbility ability) { spellAbility = ability; } - + + @Override + public ObjectColor getColor(Game game) { + if (game != null) { + CardAttribute cardAttribute = game.getState().getCardAttribute(getId()); + if (cardAttribute != null) { + return cardAttribute.getColor(); + } + } + return super.getColor(game); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/Mage/src/mage/cards/repository/CardInfo.java b/Mage/src/mage/cards/repository/CardInfo.java index 4b1c4905f13..901ab028540 100644 --- a/Mage/src/mage/cards/repository/CardInfo.java +++ b/Mage/src/mage/cards/repository/CardInfo.java @@ -132,11 +132,11 @@ public class CardInfo { this.secondSideName = secondSide.getName(); } - this.blue = card.getColor().isBlue(); - this.black = card.getColor().isBlack(); - this.green = card.getColor().isGreen(); - this.red = card.getColor().isRed(); - this.white = card.getColor().isWhite(); + this.blue = card.getColor(null).isBlue(); + this.black = card.getColor(null).isBlack(); + this.green = card.getColor(null).isGreen(); + this.red = card.getColor(null).isRed(); + this.white = card.getColor(null).isWhite(); this.setTypes(card.getCardType()); this.setSubtypes(card.getSubtype()); diff --git a/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java b/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java index 0eba7899aff..74e5164049d 100644 --- a/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/ColorPredicate.java @@ -46,7 +46,7 @@ public class ColorPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - return input.getColor().contains(color); + return input.getColor(game).contains(color); } @Override diff --git a/Mage/src/mage/filter/predicate/mageobject/ColorlessPredicate.java b/Mage/src/mage/filter/predicate/mageobject/ColorlessPredicate.java index 8a7718bf386..fa6b88d4bcd 100644 --- a/Mage/src/mage/filter/predicate/mageobject/ColorlessPredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/ColorlessPredicate.java @@ -39,7 +39,7 @@ public class ColorlessPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - return input.getColor().isColorless(); + return input.getColor(game).isColorless(); } @Override diff --git a/Mage/src/mage/filter/predicate/mageobject/MonocoloredPredicate.java b/Mage/src/mage/filter/predicate/mageobject/MonocoloredPredicate.java index f6bd95ba51c..dbb0ba54834 100644 --- a/Mage/src/mage/filter/predicate/mageobject/MonocoloredPredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/MonocoloredPredicate.java @@ -39,7 +39,7 @@ public class MonocoloredPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - return 1 == input.getColor().getColorCount(); + return 1 == input.getColor(game).getColorCount(); } @Override diff --git a/Mage/src/mage/filter/predicate/mageobject/MulticoloredPredicate.java b/Mage/src/mage/filter/predicate/mageobject/MulticoloredPredicate.java index 196ad51753a..6aca6494c25 100644 --- a/Mage/src/mage/filter/predicate/mageobject/MulticoloredPredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/MulticoloredPredicate.java @@ -39,7 +39,7 @@ public class MulticoloredPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - return 1 < input.getColor().getColorCount(); + return 1 < input.getColor(game).getColorCount(); } @Override diff --git a/Mage/src/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java b/Mage/src/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java index caf9ae4306a..8633e960971 100644 --- a/Mage/src/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/SharesColorWithSourcePredicate.java @@ -22,7 +22,7 @@ public class SharesColorWithSourcePredicate implements ObjectSourcePlayerPredica public boolean apply(ObjectSourcePlayer input, Game game) { MageObject sourceObject = game.getObject(input.getSourceId()); if (sourceObject != null) { - return input.getObject().getColor().shares(sourceObject.getColor()); + return input.getObject().getColor(game).shares(sourceObject.getColor(game)); } return false; diff --git a/Mage/src/mage/game/CardAttribute.java b/Mage/src/mage/game/CardAttribute.java new file mode 100644 index 00000000000..95b4659ab7d --- /dev/null +++ b/Mage/src/mage/game/CardAttribute.java @@ -0,0 +1,29 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.game; + +import java.io.Serializable; +import mage.ObjectColor; +import mage.cards.Card; + +/** + * This class saves changed attributes of cards (e.g. in graveyard, exile or player hands or libraries). + * + * @author LevelX2 + */ +public class CardAttribute implements Serializable { + + protected ObjectColor color; + + public CardAttribute(Card card) { + color = card.getColor(null).copy(); + } + + public ObjectColor getColor() { + return color; + } + +} diff --git a/Mage/src/mage/game/GameState.java b/Mage/src/mage/game/GameState.java index 7d6d7eb7f8d..ebfdc555a19 100644 --- a/Mage/src/mage/game/GameState.java +++ b/Mage/src/mage/game/GameState.java @@ -109,6 +109,7 @@ public class GameState implements Serializable, Copyable { private Map zones = new HashMap<>(); private List simultaneousEvents = new ArrayList<>(); private Map cardState = new HashMap<>(); + private Map cardAttribute = new HashMap<>(); private Map zoneChangeCounter = new HashMap<>(); private Map copiedCards = new HashMap<>(); private int permanentOrderNumber; @@ -831,6 +832,7 @@ public class GameState implements Serializable, Copyable { for (CardState state: cardState.values()) { state.clearAbilities(); } + cardAttribute.clear(); } public void clear() { @@ -892,11 +894,23 @@ public class GameState implements Serializable, Copyable { public CardState getCardState(UUID cardId) { if (!cardState.containsKey(cardId)) { cardState.put(cardId, new CardState()); - // cardState.putIfAbsent(cardId, new CardState()); } return cardState.get(cardId); } + public CardAttribute getCardAttribute(UUID cardId) { + return cardAttribute.get(cardId); + } + + public CardAttribute getCreateCardAttribute(Card card) { + CardAttribute cardAtt = cardAttribute.get(card.getId()); + if (cardAtt == null) { + cardAtt = new CardAttribute(card); + cardAttribute.put(card.getId(), cardAtt); + } + return cardAtt; + } + public void addWatcher(Watcher watcher) { this.watchers.add(watcher); } diff --git a/Mage/src/mage/game/command/Commander.java b/Mage/src/mage/game/command/Commander.java index 0602bafbec1..ff0a5565263 100644 --- a/Mage/src/mage/game/command/Commander.java +++ b/Mage/src/mage/game/command/Commander.java @@ -149,8 +149,8 @@ public class Commander implements CommandObject{ } @Override - public ObjectColor getColor() { - return card.getColor(); + public ObjectColor getColor(Game game) { + return card.getColor(game); } @Override diff --git a/Mage/src/mage/game/command/Emblem.java b/Mage/src/mage/game/command/Emblem.java index 468aa9cc730..79d74ca8885 100644 --- a/Mage/src/mage/game/command/Emblem.java +++ b/Mage/src/mage/game/command/Emblem.java @@ -150,7 +150,7 @@ public class Emblem implements CommandObject { } @Override - public ObjectColor getColor() { + public ObjectColor getColor(Game game) { return emptyColor; } diff --git a/Mage/src/mage/game/permanent/PermanentCard.java b/Mage/src/mage/game/permanent/PermanentCard.java index 220905b133e..c6994cb72ed 100644 --- a/Mage/src/mage/game/permanent/PermanentCard.java +++ b/Mage/src/mage/game/permanent/PermanentCard.java @@ -99,7 +99,7 @@ public class PermanentCard extends PermanentImpl { this.abilities.setSourceId(objectId); this.cardType.clear(); this.cardType.addAll(card.getCardType()); - this.color = card.getColor().copy(); + this.color = card.getColor(null).copy(); this.manaCost = card.getManaCost().copy(); this.power = card.getPower().copy(); this.toughness = card.getToughness().copy(); diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 5bfc16c5428..e50d04cb09f 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -38,6 +38,7 @@ import java.util.Map; import java.util.UUID; import mage.MageObject; import mage.MageObjectReference; +import mage.ObjectColor; import mage.abilities.Abilities; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffect; @@ -1359,5 +1360,11 @@ public abstract class PermanentImpl extends CardImpl implements Permanent { public void setCreateOrder(int createOrder) { this.createOrder = createOrder; } - + + @Override + public ObjectColor getColor(Game game) { + return color; + } + + } diff --git a/Mage/src/mage/game/permanent/PermanentToken.java b/Mage/src/mage/game/permanent/PermanentToken.java index 69d2aa37442..be5e63776a0 100644 --- a/Mage/src/mage/game/permanent/PermanentToken.java +++ b/Mage/src/mage/game/permanent/PermanentToken.java @@ -82,7 +82,7 @@ public class PermanentToken extends PermanentImpl { this.getManaCost().add(cost.copy()); } this.cardType = token.getCardType(); - this.color = token.getColor().copy(); + this.color = token.getColor(game).copy(); this.power.initValue(token.getPower().getValue()); this.toughness.initValue(token.getToughness().getValue()); this.supertype = token.getSupertype(); diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index 7fe9215ad07..bbb1ef01d8f 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -81,7 +81,7 @@ public class Spell extends StackObjImpl implements Card { public Spell(Card card, SpellAbility ability, UUID controllerId, Zone fromZone) { this.card = card; - this.color = card.getColor().copy(); + this.color = card.getColor(null).copy(); id = ability.getId(); this.ability = ability; this.ability.setControllerId(controllerId); @@ -431,7 +431,7 @@ public class Spell extends StackObjImpl implements Card { } @Override - public ObjectColor getColor() { + public ObjectColor getColor(Game game) { return color; } diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index 835d8bf1014..021ac659dbe 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -175,7 +175,7 @@ public class StackAbility extends StackObjImpl implements Ability { } @Override - public ObjectColor getColor() { + public ObjectColor getColor(Game game) { return emptyColor; } diff --git a/Mage/src/mage/util/GameLog.java b/Mage/src/mage/util/GameLog.java index b302a09d3dc..082178a1168 100644 --- a/Mage/src/mage/util/GameLog.java +++ b/Mage/src/mage/util/GameLog.java @@ -56,7 +56,7 @@ public class GameLog { } public static String getColoredObjectName(MageObject mageObject) { - return "" + mageObject.getName() + " ["+mageObject.getId().toString().substring(0,3) + "]"; + return "" + mageObject.getName() + " ["+mageObject.getId().toString().substring(0,3) + "]"; } public static String getNeutralColoredText(String text) { diff --git a/Mage/src/mage/util/functions/CopyTokenFunction.java b/Mage/src/mage/util/functions/CopyTokenFunction.java index 28e737801b1..001c8188202 100644 --- a/Mage/src/mage/util/functions/CopyTokenFunction.java +++ b/Mage/src/mage/util/functions/CopyTokenFunction.java @@ -83,7 +83,7 @@ public class CopyTokenFunction implements Function { } target.setName(sourceObj.getName()); - target.getColor().setColor(sourceObj.getColor()); + target.getColor(null).setColor(sourceObj.getColor(null)); target.getManaCost().clear(); target.getManaCost().add(sourceObj.getManaCost()); target.getCardType().clear(); diff --git a/Mage/src/mage/util/trace/TraceUtil.java b/Mage/src/mage/util/trace/TraceUtil.java index 7c413213b85..b4da8fb9850 100644 --- a/Mage/src/mage/util/trace/TraceUtil.java +++ b/Mage/src/mage/util/trace/TraceUtil.java @@ -59,7 +59,7 @@ public class TraceUtil { for (UUID blockerId : group.getBlockers()) { Permanent blocker = game.getPermanent(blockerId); if (blocker != null && !blocker.getCardType().contains(CardType.ARTIFACT) - && !attacker.getColor().shares(blocker.getColor())) { + && !attacker.getColor(game).shares(blocker.getColor(game))) { log.warn("Found creature with intimidate blocked by non artifact not sharing color creature"); traceCombat(game, attacker, blocker); } From 4543aac30e0f03980ed76c33fa3f456dc6c64440 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 10 Jun 2015 08:09:45 +0200 Subject: [PATCH 085/102] Overwrote checkEventType for Death Pits of Rath. --- .../src/mage/sets/tempest/DeathPitsOfRath.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/sets/tempest/DeathPitsOfRath.java b/Mage.Sets/src/mage/sets/tempest/DeathPitsOfRath.java index f1c4a314bfa..1af8d3cb656 100644 --- a/Mage.Sets/src/mage/sets/tempest/DeathPitsOfRath.java +++ b/Mage.Sets/src/mage/sets/tempest/DeathPitsOfRath.java @@ -50,7 +50,6 @@ public class DeathPitsOfRath extends CardImpl { super(ownerId, 21, "Death Pits of Rath", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{3}{B}{B}"); this.expansionSetCode = "TMP"; - // Whenever a creature is dealt damage, destroy it. It can't be regenerated. this.addAbility(new DeathPitsOfRathTriggeredAbility()); } @@ -80,16 +79,17 @@ class DeathPitsOfRathTriggeredAbility extends TriggeredAbilityImpl { return new DeathPitsOfRathTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) { - for(Effect effect : this.getEffects()) - { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); - } - return true; + for(Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getTargetId())); } - return false; + return true; } @Override From 4a4a949f3fade8cb560e7c9504a0fd37a6c32cc0 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 10 Jun 2015 12:57:50 +0200 Subject: [PATCH 086/102] * Helm of Possession - Added missing activation cost of {2}. --- Mage.Sets/src/mage/sets/tempest/HelmOfPossession.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage.Sets/src/mage/sets/tempest/HelmOfPossession.java b/Mage.Sets/src/mage/sets/tempest/HelmOfPossession.java index 7619182d725..f80c7843013 100644 --- a/Mage.Sets/src/mage/sets/tempest/HelmOfPossession.java +++ b/Mage.Sets/src/mage/sets/tempest/HelmOfPossession.java @@ -34,6 +34,7 @@ import mage.abilities.common.SkipUntapOptionalAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.cards.CardImpl; @@ -64,7 +65,8 @@ public class HelmOfPossession extends CardImpl { new GainControlTargetEffect(Duration.Custom), new HelmOfPossessionCondition(), "Gain control of target creature for as long as you control {this} and {this} remains tapped"); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new GenericManaCost(2)); + ability.addCost(new TapSourceCost()); ability.addTarget(new TargetCreaturePermanent()); ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent())); this.addAbility(ability); From 199ff16c0cbd3349f014f6c4788ec72f68d2fdd8 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 10 Jun 2015 13:26:49 +0200 Subject: [PATCH 087/102] * Erratic Portal - Fixed that the mana costs had not to be paid if the players said he wants to pay. --- .../sets/alarareborn/VectisDominator.java | 2 +- .../src/mage/sets/conspiracy/Victimize.java | 9 +++---- .../src/mage/sets/exodus/ErraticPortal.java | 27 ++++++++++--------- .../src/mage/sets/exodus/ExaltedDragon.java | 2 +- .../sets/fifthdawn/PlungeIntoDarkness.java | 2 +- .../src/mage/sets/mirrodin/CrystalShard.java | 26 ++++++++++-------- .../src/mage/sets/shadowmoor/Tyrannize.java | 2 +- 7 files changed, 38 insertions(+), 32 deletions(-) diff --git a/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java b/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java index dc20a77d6d2..e315a53fe22 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java +++ b/Mage.Sets/src/mage/sets/alarareborn/VectisDominator.java @@ -103,7 +103,7 @@ class VectisDominatorEffect extends OneShotEffect { Player player = game.getPlayer(targetCreature.getControllerId()); if (player != null) { cost.clearPaid(); - final StringBuilder sb = new StringBuilder("Pay 2 life otherwise ").append(targetCreature.getName()).append(" will be tapped)"); + final StringBuilder sb = new StringBuilder("Pay 2 life? (Otherwise ").append(targetCreature.getName()).append(" will be tapped)"); if (player.chooseUse(Outcome.Benefit, sb.toString(), game)) { cost.pay(source, game, targetCreature.getControllerId(), targetCreature.getControllerId(), true); } diff --git a/Mage.Sets/src/mage/sets/conspiracy/Victimize.java b/Mage.Sets/src/mage/sets/conspiracy/Victimize.java index fe071b297d4..5f8a7bf3fc4 100644 --- a/Mage.Sets/src/mage/sets/conspiracy/Victimize.java +++ b/Mage.Sets/src/mage/sets/conspiracy/Victimize.java @@ -54,7 +54,6 @@ public class Victimize extends CardImpl { super(ownerId, 133, "Victimize", Rarity.UNCOMMON, new CardType[]{CardType.SORCERY}, "{2}{B}"); this.expansionSetCode = "CNS"; - // Choose two target creature cards in your graveyard. Sacrifice a creature. If you do, return the chosen cards to the battlefield tapped. this.getSpellAbility().addEffect(new VictimizeEffect()); this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(2, new FilterCreatureCard("creature cards in your graveyard"))); @@ -88,14 +87,14 @@ class VictimizeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { SacrificeTargetCost cost = new SacrificeTargetCost(new TargetControlledCreaturePermanent(new FilterControlledCreaturePermanent("a creature"))); - if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), true)) { + if (cost.pay(source, game, source.getSourceId(), source.getControllerId(), false)) { for (UUID targetId: getTargetPointer().getTargets(game, source)) { Card card = game.getCard(targetId); if (card != null) { - player.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId(), true); + controller.putOntoBattlefieldWithInfo(card, game, Zone.GRAVEYARD, source.getSourceId(), true); } } } diff --git a/Mage.Sets/src/mage/sets/exodus/ErraticPortal.java b/Mage.Sets/src/mage/sets/exodus/ErraticPortal.java index 0e30181ea6e..49b4ef3f46b 100644 --- a/Mage.Sets/src/mage/sets/exodus/ErraticPortal.java +++ b/Mage.Sets/src/mage/sets/exodus/ErraticPortal.java @@ -95,19 +95,22 @@ class ErraticPortalEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent targetCreature = game.getPermanent(source.getFirstTarget()); - if (targetCreature != null) { - Player player = game.getPlayer(targetCreature.getControllerId()); - if (player != null) { - cost.clearPaid(); - final StringBuilder sb = new StringBuilder("Pay {1} otherwise ").append(targetCreature.getName()).append(" will be returned to its owner's hand)"); - if (player.chooseUse(Outcome.Benefit, sb.toString(), game)) { - cost.pay(source, game, targetCreature.getControllerId(), targetCreature.getControllerId(), true); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (targetCreature != null) { + Player player = game.getPlayer(targetCreature.getControllerId()); + if (player != null) { + cost.clearPaid(); + if (player.chooseUse(Outcome.Benefit, "Pay {1}? (Otherwise " + targetCreature.getLogName() +" will be returned to its owner's hand)", game)) { + cost.pay(source, game, targetCreature.getControllerId(), targetCreature.getControllerId(), false); + } + if (!cost.isPaid()) { + controller.moveCards(targetCreature, Zone.BATTLEFIELD, Zone.HAND, source, game); + } } - if (!cost.isPaid()) { - return targetCreature.moveToZone(Zone.HAND, source.getSourceId(), game, true); - } - } + } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/exodus/ExaltedDragon.java b/Mage.Sets/src/mage/sets/exodus/ExaltedDragon.java index e0931a73e79..a68578c0a68 100644 --- a/Mage.Sets/src/mage/sets/exodus/ExaltedDragon.java +++ b/Mage.Sets/src/mage/sets/exodus/ExaltedDragon.java @@ -105,7 +105,7 @@ class ExaltedDragonReplacementEffect extends ReplacementEffectImpl { if ( attackCost.canPay(source, source.getSourceId(), event.getPlayerId(), game) && player.chooseUse(Outcome.Neutral, "Sacrifice a land?", game) ) { - if (attackCost.pay(source, game, source.getSourceId(), event.getPlayerId(), true) ) { + if (attackCost.pay(source, game, source.getSourceId(), event.getPlayerId(), false) ) { return false; } } diff --git a/Mage.Sets/src/mage/sets/fifthdawn/PlungeIntoDarkness.java b/Mage.Sets/src/mage/sets/fifthdawn/PlungeIntoDarkness.java index 8e3b700106f..1d8c1a7430f 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/PlungeIntoDarkness.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/PlungeIntoDarkness.java @@ -148,7 +148,7 @@ class PlungeIntoDarknessSearchEffect extends OneShotEffect { if (player != null) { VariableCost cost = new PayVariableLifeCost(); int xValue = cost.announceXValue(source, game); - cost.getFixedCostsFromAnnouncedValue(xValue).pay(source, game, source.getSourceId(), source.getControllerId(), true); + cost.getFixedCostsFromAnnouncedValue(xValue).pay(source, game, source.getSourceId(), source.getControllerId(), false); Cards cards = new CardsImpl(Zone.PICK); int count = Math.min(player.getLibrary().size(), xValue); diff --git a/Mage.Sets/src/mage/sets/mirrodin/CrystalShard.java b/Mage.Sets/src/mage/sets/mirrodin/CrystalShard.java index b5d00bc2846..03a2101ae59 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/CrystalShard.java +++ b/Mage.Sets/src/mage/sets/mirrodin/CrystalShard.java @@ -98,19 +98,23 @@ class CrystalShardEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Permanent targetCreature = game.getPermanent(source.getFirstTarget()); - if (targetCreature != null) { - Player player = game.getPlayer(targetCreature.getControllerId()); - if (player != null) { - cost.clearPaid(); - final StringBuilder sb = new StringBuilder("Pay {1} otherwise ").append(targetCreature.getName()).append(" will be returned to its owner's hand)"); - if (player.chooseUse(Outcome.Benefit, sb.toString(), game)) { - cost.pay(source, game, targetCreature.getControllerId(), targetCreature.getControllerId(), false); - } - if (!cost.isPaid()) { - return targetCreature.moveToZone(Zone.HAND, source.getSourceId(), game, true); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source)); + if (targetCreature != null) { + Player player = game.getPlayer(targetCreature.getControllerId()); + if (player != null) { + cost.clearPaid(); + final StringBuilder sb = new StringBuilder("Pay {1}? (Otherwise ").append(targetCreature.getName()).append(" will be returned to its owner's hand)"); + if (player.chooseUse(Outcome.Benefit, sb.toString(), game)) { + cost.pay(source, game, targetCreature.getControllerId(), targetCreature.getControllerId(), false); + } + if (!cost.isPaid()) { + controller.moveCards(targetCreature, Zone.BATTLEFIELD, Zone.HAND, source, game); + } } } + return true; } return false; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/Tyrannize.java b/Mage.Sets/src/mage/sets/shadowmoor/Tyrannize.java index d075c7ddfad..cc9985ed412 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/Tyrannize.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/Tyrannize.java @@ -92,7 +92,7 @@ class TyrannizeEffect extends OneShotEffect { Cost cost = new PayLifeCost(7); if (!cost.canPay(source, source.getSourceId(), player.getId(), game) || !player.chooseUse(Outcome.LoseLife, "Pay 7 life?", game) - || !cost.pay(source, game, source.getSourceId(), player.getId(), true)) { + || !cost.pay(source, game, source.getSourceId(), player.getId(), false)) { for (Card card : player.getHand().getCards(game)) { player.discard(card, source, game); } From 93dcf2d59f6dc2020e03bb3dba2e6cc49be590b4 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 10 Jun 2015 14:00:37 +0200 Subject: [PATCH 088/102] * Finished card attribute change handling for color of cards (card subtype not supported yet) related to #408 --- Mage.Sets/src/mage/sets/Conspiracy.java | 1 - .../mage/sets/shadowmoor/CeruleanWisps.java | 1 + .../mage/sets/tenthedition/DragonsClaw.java | 14 +-- .../cards/continuous/PaintersServantTest.java | 93 +++++++++++++++++++ .../abilities/effects/ContinuousEffects.java | 2 +- .../continuous/BecomesColorTargetEffect.java | 54 ++++++----- 6 files changed, 131 insertions(+), 34 deletions(-) diff --git a/Mage.Sets/src/mage/sets/Conspiracy.java b/Mage.Sets/src/mage/sets/Conspiracy.java index cb0ad96e7c7..1cf7af1408a 100644 --- a/Mage.Sets/src/mage/sets/Conspiracy.java +++ b/Mage.Sets/src/mage/sets/Conspiracy.java @@ -37,7 +37,6 @@ import mage.constants.SetType; * @author LevelX2 */ - public class Conspiracy extends ExpansionSet { private static final Conspiracy fINSTANCE = new Conspiracy(); diff --git a/Mage.Sets/src/mage/sets/shadowmoor/CeruleanWisps.java b/Mage.Sets/src/mage/sets/shadowmoor/CeruleanWisps.java index dcecf07ef75..c3b478e635c 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/CeruleanWisps.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/CeruleanWisps.java @@ -54,6 +54,7 @@ public class CeruleanWisps extends CardImpl { this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addEffect(new BecomesColorTargetEffect(ObjectColor.BLUE, Duration.EndOfTurn)); this.getSpellAbility().addEffect(new UntapTargetEffect()); + // Draw a card. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1)); } diff --git a/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java b/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java index 3fb21d7d48c..86a28f2dd3c 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java +++ b/Mage.Sets/src/mage/sets/tenthedition/DragonsClaw.java @@ -78,15 +78,15 @@ class DragonsClawAbility extends TriggeredAbilityImpl { return new DragonsClawAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == EventType.SPELL_CAST; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == EventType.SPELL_CAST) { - Spell spell = game.getStack().getSpell(event.getTargetId()); - if (spell != null && spell.getColor(game).isRed()) { - return true; - } - } - return false; + Spell spell = game.getStack().getSpell(event.getTargetId()); + return spell != null && spell.getColor(game).isRed(); } @Override diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java index 9ad87ebb8c5..2a45bf0f4a6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/PaintersServantTest.java @@ -75,6 +75,13 @@ public class PaintersServantTest extends CardTestPlayerBase { Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite()); Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue()); + for(Card card: playerA.getLibrary().getCards(currentGame)) { + Assert.assertEquals(card.getName() + " should be blue",true, card.getColor(currentGame).isBlue()); + } + for(Card card: playerB.getLibrary().getCards(currentGame)) { + Assert.assertEquals(card.getName() + " should be blue",true, card.getColor(currentGame).isBlue()); + } + for(Card card: playerA.getHand().getCards(currentGame)) { Assert.assertEquals(true, card.getColor(currentGame).isRed()); Assert.assertEquals(true, card.getColor(currentGame).isBlue()); @@ -131,6 +138,13 @@ public class PaintersServantTest extends CardTestPlayerBase { Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isWhite()); Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isBlue()); + for(Card card: playerA.getLibrary().getCards(currentGame)) { + Assert.assertEquals(card.getName() + " should not be blue",false, card.getColor(currentGame).isBlue()); + } + for(Card card: playerB.getLibrary().getCards(currentGame)) { + Assert.assertEquals(card.getName() + " should not be blue",false, card.getColor(currentGame).isBlue()); + } + for(Card card: playerA.getHand().getCards(currentGame)) { Assert.assertEquals(true, card.getColor(currentGame).isRed()); Assert.assertEquals(false, card.getColor(currentGame).isBlue()); @@ -154,4 +168,83 @@ public class PaintersServantTest extends CardTestPlayerBase { } + /** + * 5/1/2008 While Painter's Servant is on the battlefield, an effect that + * changes an object's colors will overwrite Painter's Servant's effect. For + * example, casting Cerulean Wisps on a creature will turn it blue, + * regardless of the color chosen for Painter's Servant. + */ + @Test + public void testColorOverwrite() { + // As Painter's Servant enters the battlefield, choose a color. + // All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors. + addCard(Zone.HAND, playerA, "Painter's Servant", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + // Target creature becomes blue until end of turn. Untap that creature. + // Draw a card. + addCard(Zone.HAND, playerB, "Cerulean Wisps", 1); // Instant {U} + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); + setChoice(playerA, "Red"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Cerulean Wisps", "Silvercoat Lion", "Painter's Servant", StackClause.WHILE_NOT_ON_STACK); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Painter's Servant", 1); + + Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isRed()); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue()); + } + + /** + * Check color of spells + */ + @Test + public void testColorSpell() { + // As Painter's Servant enters the battlefield, choose a color. + // All cards that aren't on the battlefield, spells, and permanents are the chosen color in addition to their other colors. + addCard(Zone.HAND, playerA, "Painter's Servant", 1); + // Draw two cards. + addCard(Zone.HAND, playerA, "Divination", 1); // {U}{2} + addCard(Zone.BATTLEFIELD, playerA, "Island", 5); + // Whenever a player casts a red spell, you may gain 1 life. + addCard(Zone.BATTLEFIELD, playerA, "Dragon's Claw"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + // Target creature becomes blue until end of turn. Untap that creature. + // Draw a card. + addCard(Zone.HAND, playerB, "Cerulean Wisps", 1); // Instant {U} + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Painter's Servant"); + setChoice(playerA, "Red"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Cerulean Wisps", "Silvercoat Lion", "Painter's Servant", StackClause.WHILE_NOT_ON_STACK); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Divination", NO_TARGET, "Painter's Servant", StackClause.WHILE_NOT_ON_STACK); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertPermanentCount(playerA, "Painter's Servant", 1); + + assertGraveyardCount(playerA, "Divination", 1); + assertGraveyardCount(playerB, "Cerulean Wisps", 1); + assertLife(playerA, 22); // + 1 from Cerulean Wisps + 1 from Divination + + Permanent silvercoatLion = getPermanent("Silvercoat Lion", playerA); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isWhite()); + Assert.assertEquals(false, silvercoatLion.getColor(currentGame).isRed()); + Assert.assertEquals(true, silvercoatLion.getColor(currentGame).isBlue()); + + + } + + } diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 561f1facc32..0b2c1fbe43b 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -803,7 +803,7 @@ public class ContinuousEffects implements Serializable { } } // Must be called here for some effects to be able to work correctly - // TODO: add info which effects that need + // TODO: add info which effects need that call game.applyEffects(); } while (true); return caught; diff --git a/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java b/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java index b93a7cfff53..975beaa6e71 100644 --- a/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continuous/BecomesColorTargetEffect.java @@ -41,8 +41,6 @@ import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.SubLayer; import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.game.stack.StackObject; import mage.players.Player; /** @@ -50,7 +48,7 @@ import mage.players.Player; */ public class BecomesColorTargetEffect extends ContinuousEffectImpl { - private final ObjectColor setColor; + private ObjectColor setColor; /** * Set the color of a spell or permanent @@ -76,49 +74,55 @@ public class BecomesColorTargetEffect extends ContinuousEffectImpl { } @Override - public boolean apply(Game game, Ability source) { + public void init(Ability source, Game game) { Player controller = game.getPlayer(source.getControllerId()); if (controller == null) { - return false; - } - boolean result = false; - ObjectColor objectColor; + return; + } if (setColor == null) { ChoiceColor choice = new ChoiceColor(); while (!choice.isChosen()) { controller.choose(Outcome.PutManaInPool, choice, game); if (!controller.isInGame()) { - return false; + return; } } if (choice.getColor() != null) { - objectColor = choice.getColor(); + setColor = choice.getColor(); } else { - return false; + return; } if (!game.isSimulation()) { - game.informPlayers(controller.getLogName() + " has chosen the color: " + objectColor.toString()); + game.informPlayers(controller.getLogName() + " has chosen the color: " + setColor.toString()); } - } else { - objectColor = this.setColor; + } + + + super.init(source, game); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + if (controller == null) { + return false; } - if (objectColor != null) { + if (setColor != null) { + boolean objectFound = false; for (UUID targetId :targetPointer.getTargets(game, source)) { - MageObject o = game.getObject(targetId); - if (o != null) { - if (o instanceof Permanent || o instanceof StackObject) { - o.getColor(game).setColor(objectColor); - result = true; - } + MageObject targetObject = game.getObject(targetId); + if (targetObject != null) { + objectFound = true; + targetObject.getColor(game).setColor(setColor); } } - } - if (!result) { - if (this.getDuration().equals(Duration.Custom)) { + if (!objectFound && this.getDuration().equals(Duration.Custom)) { this.discard(); } + return true; + } else { + throw new UnsupportedOperationException("No color set"); } - return result; } @Override From 8ae9293c9851e406efd5a8aead4825b6f2c2e404 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Wed, 10 Jun 2015 17:52:18 +0200 Subject: [PATCH 089/102] * Added user country flag. View of image in user list is still missing. --- .../src/main/java/mage/client/MageFrame.java | 23 +- .../main/java/mage/client/chat/ChatPanel.java | 15 +- .../mage/client/dialog/ConnectDialog.form | 44 ++- .../mage/client/dialog/ConnectDialog.java | 69 ++-- .../mage/client/dialog/PreferencesDialog.form | 2 +- .../mage/client/dialog/PreferencesDialog.java | 11 +- .../util/gui/countryBox/CountryComboBox.java | 315 ++++++++++++++++++ .../gui/countryBox/CountryItemEditor.java | 87 +++++ .../gui/countryBox/CountryItemRenderer.java | 87 +++++ .../util/gui/countryBox/MyComboBoxEditor.java | 77 +++++ .../gui/countryBox/MyComboBoxRenderer.java | 57 ++++ Mage.Client/src/main/resources/flags/ad.png | Bin 0 -> 643 bytes Mage.Client/src/main/resources/flags/ae.png | Bin 0 -> 408 bytes Mage.Client/src/main/resources/flags/af.png | Bin 0 -> 604 bytes Mage.Client/src/main/resources/flags/ag.png | Bin 0 -> 591 bytes Mage.Client/src/main/resources/flags/ai.png | Bin 0 -> 643 bytes Mage.Client/src/main/resources/flags/al.png | Bin 0 -> 600 bytes Mage.Client/src/main/resources/flags/am.png | Bin 0 -> 497 bytes Mage.Client/src/main/resources/flags/an.png | Bin 0 -> 488 bytes Mage.Client/src/main/resources/flags/ao.png | Bin 0 -> 428 bytes Mage.Client/src/main/resources/flags/ar.png | Bin 0 -> 506 bytes Mage.Client/src/main/resources/flags/as.png | Bin 0 -> 647 bytes Mage.Client/src/main/resources/flags/at.png | Bin 0 -> 403 bytes Mage.Client/src/main/resources/flags/au.png | Bin 0 -> 673 bytes Mage.Client/src/main/resources/flags/aw.png | Bin 0 -> 524 bytes Mage.Client/src/main/resources/flags/ax.png | Bin 0 -> 663 bytes Mage.Client/src/main/resources/flags/az.png | Bin 0 -> 589 bytes Mage.Client/src/main/resources/flags/ba.png | Bin 0 -> 593 bytes Mage.Client/src/main/resources/flags/bb.png | Bin 0 -> 585 bytes Mage.Client/src/main/resources/flags/bd.png | Bin 0 -> 504 bytes Mage.Client/src/main/resources/flags/be.png | Bin 0 -> 449 bytes Mage.Client/src/main/resources/flags/bf.png | Bin 0 -> 497 bytes Mage.Client/src/main/resources/flags/bg.png | Bin 0 -> 462 bytes Mage.Client/src/main/resources/flags/bh.png | Bin 0 -> 457 bytes Mage.Client/src/main/resources/flags/bi.png | Bin 0 -> 675 bytes Mage.Client/src/main/resources/flags/bj.png | Bin 0 -> 486 bytes Mage.Client/src/main/resources/flags/bm.png | Bin 0 -> 611 bytes Mage.Client/src/main/resources/flags/bn.png | Bin 0 -> 639 bytes Mage.Client/src/main/resources/flags/bo.png | Bin 0 -> 500 bytes Mage.Client/src/main/resources/flags/br.png | Bin 0 -> 593 bytes Mage.Client/src/main/resources/flags/bs.png | Bin 0 -> 526 bytes Mage.Client/src/main/resources/flags/bt.png | Bin 0 -> 631 bytes Mage.Client/src/main/resources/flags/bv.png | Bin 0 -> 512 bytes Mage.Client/src/main/resources/flags/bw.png | Bin 0 -> 443 bytes Mage.Client/src/main/resources/flags/by.png | Bin 0 -> 514 bytes Mage.Client/src/main/resources/flags/bz.png | Bin 0 -> 600 bytes Mage.Client/src/main/resources/flags/ca.png | Bin 0 -> 628 bytes .../src/main/resources/flags/catalonia.png | Bin 0 -> 398 bytes Mage.Client/src/main/resources/flags/cc.png | Bin 0 -> 625 bytes Mage.Client/src/main/resources/flags/cd.png | Bin 0 -> 528 bytes Mage.Client/src/main/resources/flags/cf.png | Bin 0 -> 614 bytes Mage.Client/src/main/resources/flags/cg.png | Bin 0 -> 521 bytes Mage.Client/src/main/resources/flags/ch.png | Bin 0 -> 367 bytes Mage.Client/src/main/resources/flags/ci.png | Bin 0 -> 453 bytes Mage.Client/src/main/resources/flags/ck.png | Bin 0 -> 586 bytes Mage.Client/src/main/resources/flags/cl.png | Bin 0 -> 450 bytes Mage.Client/src/main/resources/flags/cm.png | Bin 0 -> 525 bytes Mage.Client/src/main/resources/flags/cn.png | Bin 0 -> 472 bytes Mage.Client/src/main/resources/flags/co.png | Bin 0 -> 483 bytes Mage.Client/src/main/resources/flags/cr.png | Bin 0 -> 477 bytes Mage.Client/src/main/resources/flags/cs.png | Bin 0 -> 439 bytes Mage.Client/src/main/resources/flags/cu.png | Bin 0 -> 563 bytes Mage.Client/src/main/resources/flags/cv.png | Bin 0 -> 529 bytes Mage.Client/src/main/resources/flags/cx.png | Bin 0 -> 608 bytes Mage.Client/src/main/resources/flags/cy.png | Bin 0 -> 428 bytes Mage.Client/src/main/resources/flags/cz.png | Bin 0 -> 476 bytes Mage.Client/src/main/resources/flags/de.png | Bin 0 -> 545 bytes Mage.Client/src/main/resources/flags/dj.png | Bin 0 -> 572 bytes Mage.Client/src/main/resources/flags/dk.png | Bin 0 -> 495 bytes Mage.Client/src/main/resources/flags/dm.png | Bin 0 -> 620 bytes Mage.Client/src/main/resources/flags/do.png | Bin 0 -> 508 bytes Mage.Client/src/main/resources/flags/dz.png | Bin 0 -> 582 bytes Mage.Client/src/main/resources/flags/ec.png | Bin 0 -> 500 bytes Mage.Client/src/main/resources/flags/ee.png | Bin 0 -> 429 bytes Mage.Client/src/main/resources/flags/eg.png | Bin 0 -> 465 bytes Mage.Client/src/main/resources/flags/eh.png | Bin 0 -> 508 bytes .../src/main/resources/flags/england.png | Bin 0 -> 496 bytes Mage.Client/src/main/resources/flags/er.png | Bin 0 -> 653 bytes Mage.Client/src/main/resources/flags/es.png | Bin 0 -> 469 bytes Mage.Client/src/main/resources/flags/et.png | Bin 0 -> 592 bytes .../main/resources/flags/europeanunion.png | Bin 0 -> 479 bytes Mage.Client/src/main/resources/flags/fi.png | Bin 0 -> 489 bytes Mage.Client/src/main/resources/flags/fj.png | Bin 0 -> 610 bytes Mage.Client/src/main/resources/flags/fk.png | Bin 0 -> 648 bytes Mage.Client/src/main/resources/flags/fm.png | Bin 0 -> 552 bytes Mage.Client/src/main/resources/flags/fo.png | Bin 0 -> 474 bytes Mage.Client/src/main/resources/flags/fr.png | Bin 0 -> 545 bytes Mage.Client/src/main/resources/flags/ga.png | Bin 0 -> 489 bytes Mage.Client/src/main/resources/flags/gb.png | Bin 0 -> 599 bytes Mage.Client/src/main/resources/flags/gd.png | Bin 0 -> 637 bytes Mage.Client/src/main/resources/flags/ge.png | Bin 0 -> 594 bytes Mage.Client/src/main/resources/flags/gf.png | Bin 0 -> 545 bytes Mage.Client/src/main/resources/flags/gh.png | Bin 0 -> 490 bytes Mage.Client/src/main/resources/flags/gi.png | Bin 0 -> 463 bytes Mage.Client/src/main/resources/flags/gl.png | Bin 0 -> 470 bytes Mage.Client/src/main/resources/flags/gm.png | Bin 0 -> 493 bytes Mage.Client/src/main/resources/flags/gn.png | Bin 0 -> 480 bytes Mage.Client/src/main/resources/flags/gp.png | Bin 0 -> 488 bytes Mage.Client/src/main/resources/flags/gq.png | Bin 0 -> 537 bytes Mage.Client/src/main/resources/flags/gr.png | Bin 0 -> 487 bytes Mage.Client/src/main/resources/flags/gs.png | Bin 0 -> 630 bytes Mage.Client/src/main/resources/flags/gt.png | Bin 0 -> 493 bytes Mage.Client/src/main/resources/flags/gu.png | Bin 0 -> 509 bytes Mage.Client/src/main/resources/flags/gw.png | Bin 0 -> 516 bytes Mage.Client/src/main/resources/flags/gy.png | Bin 0 -> 645 bytes Mage.Client/src/main/resources/flags/hk.png | Bin 0 -> 527 bytes Mage.Client/src/main/resources/flags/hm.png | Bin 0 -> 673 bytes Mage.Client/src/main/resources/flags/hn.png | Bin 0 -> 537 bytes Mage.Client/src/main/resources/flags/hr.png | Bin 0 -> 524 bytes Mage.Client/src/main/resources/flags/ht.png | Bin 0 -> 487 bytes Mage.Client/src/main/resources/flags/hu.png | Bin 0 -> 432 bytes Mage.Client/src/main/resources/flags/id.png | Bin 0 -> 430 bytes Mage.Client/src/main/resources/flags/ie.png | Bin 0 -> 481 bytes Mage.Client/src/main/resources/flags/il.png | Bin 0 -> 431 bytes Mage.Client/src/main/resources/flags/in.png | Bin 0 -> 503 bytes Mage.Client/src/main/resources/flags/io.png | Bin 0 -> 658 bytes Mage.Client/src/main/resources/flags/iq.png | Bin 0 -> 515 bytes Mage.Client/src/main/resources/flags/ir.png | Bin 0 -> 512 bytes Mage.Client/src/main/resources/flags/is.png | Bin 0 -> 532 bytes Mage.Client/src/main/resources/flags/it.png | Bin 0 -> 420 bytes Mage.Client/src/main/resources/flags/jm.png | Bin 0 -> 637 bytes Mage.Client/src/main/resources/flags/jo.png | Bin 0 -> 473 bytes Mage.Client/src/main/resources/flags/jp.png | Bin 0 -> 420 bytes Mage.Client/src/main/resources/flags/ke.png | Bin 0 -> 569 bytes Mage.Client/src/main/resources/flags/kg.png | Bin 0 -> 510 bytes Mage.Client/src/main/resources/flags/kh.png | Bin 0 -> 549 bytes Mage.Client/src/main/resources/flags/ki.png | Bin 0 -> 656 bytes Mage.Client/src/main/resources/flags/km.png | Bin 0 -> 577 bytes Mage.Client/src/main/resources/flags/kn.png | Bin 0 -> 604 bytes Mage.Client/src/main/resources/flags/kp.png | Bin 0 -> 561 bytes Mage.Client/src/main/resources/flags/kr.png | Bin 0 -> 592 bytes Mage.Client/src/main/resources/flags/kw.png | Bin 0 -> 486 bytes Mage.Client/src/main/resources/flags/ky.png | Bin 0 -> 643 bytes Mage.Client/src/main/resources/flags/kz.png | Bin 0 -> 616 bytes Mage.Client/src/main/resources/flags/la.png | Bin 0 -> 563 bytes Mage.Client/src/main/resources/flags/lb.png | Bin 0 -> 517 bytes Mage.Client/src/main/resources/flags/lc.png | Bin 0 -> 520 bytes Mage.Client/src/main/resources/flags/li.png | Bin 0 -> 537 bytes Mage.Client/src/main/resources/flags/lk.png | Bin 0 -> 627 bytes Mage.Client/src/main/resources/flags/lr.png | Bin 0 -> 466 bytes Mage.Client/src/main/resources/flags/ls.png | Bin 0 -> 628 bytes Mage.Client/src/main/resources/flags/lt.png | Bin 0 -> 508 bytes Mage.Client/src/main/resources/flags/lu.png | Bin 0 -> 481 bytes Mage.Client/src/main/resources/flags/lv.png | Bin 0 -> 465 bytes Mage.Client/src/main/resources/flags/ly.png | Bin 0 -> 419 bytes Mage.Client/src/main/resources/flags/ma.png | Bin 0 -> 432 bytes Mage.Client/src/main/resources/flags/mc.png | Bin 0 -> 380 bytes Mage.Client/src/main/resources/flags/md.png | Bin 0 -> 566 bytes Mage.Client/src/main/resources/flags/me.png | Bin 0 -> 448 bytes Mage.Client/src/main/resources/flags/mg.png | Bin 0 -> 453 bytes Mage.Client/src/main/resources/flags/mh.png | Bin 0 -> 628 bytes Mage.Client/src/main/resources/flags/mk.png | Bin 0 -> 664 bytes Mage.Client/src/main/resources/flags/ml.png | Bin 0 -> 474 bytes Mage.Client/src/main/resources/flags/mm.png | Bin 0 -> 483 bytes Mage.Client/src/main/resources/flags/mn.png | Bin 0 -> 492 bytes Mage.Client/src/main/resources/flags/mo.png | Bin 0 -> 588 bytes Mage.Client/src/main/resources/flags/mp.png | Bin 0 -> 597 bytes Mage.Client/src/main/resources/flags/mq.png | Bin 0 -> 655 bytes Mage.Client/src/main/resources/flags/mr.png | Bin 0 -> 569 bytes Mage.Client/src/main/resources/flags/ms.png | Bin 0 -> 614 bytes Mage.Client/src/main/resources/flags/mt.png | Bin 0 -> 420 bytes Mage.Client/src/main/resources/flags/mu.png | Bin 0 -> 496 bytes Mage.Client/src/main/resources/flags/mv.png | Bin 0 -> 542 bytes Mage.Client/src/main/resources/flags/mw.png | Bin 0 -> 529 bytes Mage.Client/src/main/resources/flags/mx.png | Bin 0 -> 574 bytes Mage.Client/src/main/resources/flags/my.png | Bin 0 -> 571 bytes Mage.Client/src/main/resources/flags/mz.png | Bin 0 -> 584 bytes Mage.Client/src/main/resources/flags/na.png | Bin 0 -> 647 bytes Mage.Client/src/main/resources/flags/nc.png | Bin 0 -> 591 bytes Mage.Client/src/main/resources/flags/ne.png | Bin 0 -> 537 bytes Mage.Client/src/main/resources/flags/nf.png | Bin 0 -> 602 bytes Mage.Client/src/main/resources/flags/ng.png | Bin 0 -> 482 bytes Mage.Client/src/main/resources/flags/ni.png | Bin 0 -> 508 bytes Mage.Client/src/main/resources/flags/nl.png | Bin 0 -> 453 bytes Mage.Client/src/main/resources/flags/no.png | Bin 0 -> 512 bytes Mage.Client/src/main/resources/flags/np.png | Bin 0 -> 443 bytes Mage.Client/src/main/resources/flags/nr.png | Bin 0 -> 527 bytes Mage.Client/src/main/resources/flags/nu.png | Bin 0 -> 572 bytes Mage.Client/src/main/resources/flags/nz.png | Bin 0 -> 639 bytes Mage.Client/src/main/resources/flags/om.png | Bin 0 -> 478 bytes Mage.Client/src/main/resources/flags/pa.png | Bin 0 -> 519 bytes Mage.Client/src/main/resources/flags/pe.png | Bin 0 -> 397 bytes Mage.Client/src/main/resources/flags/pf.png | Bin 0 -> 498 bytes Mage.Client/src/main/resources/flags/pg.png | Bin 0 -> 593 bytes Mage.Client/src/main/resources/flags/ph.png | Bin 0 -> 538 bytes Mage.Client/src/main/resources/flags/pk.png | Bin 0 -> 569 bytes Mage.Client/src/main/resources/flags/pl.png | Bin 0 -> 374 bytes Mage.Client/src/main/resources/flags/pm.png | Bin 0 -> 689 bytes Mage.Client/src/main/resources/flags/pn.png | Bin 0 -> 657 bytes Mage.Client/src/main/resources/flags/pr.png | Bin 0 -> 556 bytes Mage.Client/src/main/resources/flags/ps.png | Bin 0 -> 472 bytes Mage.Client/src/main/resources/flags/pt.png | Bin 0 -> 554 bytes Mage.Client/src/main/resources/flags/pw.png | Bin 0 -> 550 bytes Mage.Client/src/main/resources/flags/py.png | Bin 0 -> 473 bytes Mage.Client/src/main/resources/flags/qa.png | Bin 0 -> 450 bytes Mage.Client/src/main/resources/flags/re.png | Bin 0 -> 545 bytes Mage.Client/src/main/resources/flags/ro.png | Bin 0 -> 495 bytes Mage.Client/src/main/resources/flags/rs.png | Bin 0 -> 423 bytes Mage.Client/src/main/resources/flags/ru.png | Bin 0 -> 420 bytes Mage.Client/src/main/resources/flags/rw.png | Bin 0 -> 533 bytes Mage.Client/src/main/resources/flags/sa.png | Bin 0 -> 551 bytes Mage.Client/src/main/resources/flags/sb.png | Bin 0 -> 624 bytes Mage.Client/src/main/resources/flags/sc.png | Bin 0 -> 608 bytes .../src/main/resources/flags/scotland.png | Bin 0 -> 649 bytes Mage.Client/src/main/resources/flags/sd.png | Bin 0 -> 492 bytes Mage.Client/src/main/resources/flags/se.png | Bin 0 -> 542 bytes Mage.Client/src/main/resources/flags/sg.png | Bin 0 -> 468 bytes Mage.Client/src/main/resources/flags/sh.png | Bin 0 -> 645 bytes Mage.Client/src/main/resources/flags/si.png | Bin 0 -> 510 bytes Mage.Client/src/main/resources/flags/sj.png | Bin 0 -> 512 bytes Mage.Client/src/main/resources/flags/sk.png | Bin 0 -> 562 bytes Mage.Client/src/main/resources/flags/sl.png | Bin 0 -> 436 bytes Mage.Client/src/main/resources/flags/sm.png | Bin 0 -> 502 bytes Mage.Client/src/main/resources/flags/sn.png | Bin 0 -> 532 bytes Mage.Client/src/main/resources/flags/so.png | Bin 0 -> 527 bytes Mage.Client/src/main/resources/flags/sr.png | Bin 0 -> 513 bytes Mage.Client/src/main/resources/flags/st.png | Bin 0 -> 584 bytes Mage.Client/src/main/resources/flags/sv.png | Bin 0 -> 501 bytes Mage.Client/src/main/resources/flags/sy.png | Bin 0 -> 422 bytes Mage.Client/src/main/resources/flags/sz.png | Bin 0 -> 643 bytes Mage.Client/src/main/resources/flags/tc.png | Bin 0 -> 624 bytes Mage.Client/src/main/resources/flags/td.png | Bin 0 -> 570 bytes Mage.Client/src/main/resources/flags/tf.png | Bin 0 -> 527 bytes Mage.Client/src/main/resources/flags/tg.png | Bin 0 -> 562 bytes Mage.Client/src/main/resources/flags/th.png | Bin 0 -> 452 bytes Mage.Client/src/main/resources/flags/tj.png | Bin 0 -> 496 bytes Mage.Client/src/main/resources/flags/tk.png | Bin 0 -> 638 bytes Mage.Client/src/main/resources/flags/tl.png | Bin 0 -> 514 bytes Mage.Client/src/main/resources/flags/tm.png | Bin 0 -> 593 bytes Mage.Client/src/main/resources/flags/tn.png | Bin 0 -> 495 bytes Mage.Client/src/main/resources/flags/to.png | Bin 0 -> 426 bytes Mage.Client/src/main/resources/flags/tr.png | Bin 0 -> 492 bytes Mage.Client/src/main/resources/flags/tt.png | Bin 0 -> 617 bytes Mage.Client/src/main/resources/flags/tv.png | Bin 0 -> 536 bytes Mage.Client/src/main/resources/flags/tw.png | Bin 0 -> 465 bytes Mage.Client/src/main/resources/flags/tz.png | Bin 0 -> 642 bytes Mage.Client/src/main/resources/flags/ua.png | Bin 0 -> 446 bytes Mage.Client/src/main/resources/flags/ug.png | Bin 0 -> 531 bytes Mage.Client/src/main/resources/flags/um.png | Bin 0 -> 571 bytes Mage.Client/src/main/resources/flags/us.png | Bin 0 -> 609 bytes Mage.Client/src/main/resources/flags/uy.png | Bin 0 -> 532 bytes Mage.Client/src/main/resources/flags/uz.png | Bin 0 -> 515 bytes Mage.Client/src/main/resources/flags/va.png | Bin 0 -> 553 bytes Mage.Client/src/main/resources/flags/vc.png | Bin 0 -> 577 bytes Mage.Client/src/main/resources/flags/ve.png | Bin 0 -> 528 bytes Mage.Client/src/main/resources/flags/vg.png | Bin 0 -> 630 bytes Mage.Client/src/main/resources/flags/vi.png | Bin 0 -> 616 bytes Mage.Client/src/main/resources/flags/vn.png | Bin 0 -> 474 bytes Mage.Client/src/main/resources/flags/vu.png | Bin 0 -> 604 bytes .../src/main/resources/flags/wales.png | Bin 0 -> 652 bytes Mage.Client/src/main/resources/flags/wf.png | Bin 0 -> 554 bytes .../src/main/resources/flags/world.png | Bin 0 -> 625 bytes Mage.Client/src/main/resources/flags/ws.png | Bin 0 -> 476 bytes Mage.Client/src/main/resources/flags/ye.png | Bin 0 -> 413 bytes Mage.Client/src/main/resources/flags/yt.png | Bin 0 -> 593 bytes Mage.Client/src/main/resources/flags/za.png | Bin 0 -> 642 bytes Mage.Client/src/main/resources/flags/zm.png | Bin 0 -> 500 bytes Mage.Client/src/main/resources/flags/zw.png | Bin 0 -> 574 bytes Mage.Common/src/mage/remote/Connection.java | 9 + Mage.Common/src/mage/remote/SessionImpl.java | 7 +- .../mage/remote/interfaces/ClientData.java | 2 +- Mage.Common/src/mage/view/PlayerView.java | 2 +- Mage.Common/src/mage/view/UserDataView.java | 9 +- Mage.Common/src/mage/view/UsersView.java | 8 +- .../java/mage/player/ai/ComputerPlayer.java | 2 +- .../src/main/java/mage/server/Session.java | 5 +- .../java/mage/server/game/GamesRoomImpl.java | 4 +- Mage/src/mage/players/net/UserData.java | 9 +- 268 files changed, 777 insertions(+), 67 deletions(-) create mode 100644 Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryComboBox.java create mode 100644 Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemEditor.java create mode 100644 Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemRenderer.java create mode 100644 Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxEditor.java create mode 100644 Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxRenderer.java create mode 100644 Mage.Client/src/main/resources/flags/ad.png create mode 100644 Mage.Client/src/main/resources/flags/ae.png create mode 100644 Mage.Client/src/main/resources/flags/af.png create mode 100644 Mage.Client/src/main/resources/flags/ag.png create mode 100644 Mage.Client/src/main/resources/flags/ai.png create mode 100644 Mage.Client/src/main/resources/flags/al.png create mode 100644 Mage.Client/src/main/resources/flags/am.png create mode 100644 Mage.Client/src/main/resources/flags/an.png create mode 100644 Mage.Client/src/main/resources/flags/ao.png create mode 100644 Mage.Client/src/main/resources/flags/ar.png create mode 100644 Mage.Client/src/main/resources/flags/as.png create mode 100644 Mage.Client/src/main/resources/flags/at.png create mode 100644 Mage.Client/src/main/resources/flags/au.png create mode 100644 Mage.Client/src/main/resources/flags/aw.png create mode 100644 Mage.Client/src/main/resources/flags/ax.png create mode 100644 Mage.Client/src/main/resources/flags/az.png create mode 100644 Mage.Client/src/main/resources/flags/ba.png create mode 100644 Mage.Client/src/main/resources/flags/bb.png create mode 100644 Mage.Client/src/main/resources/flags/bd.png create mode 100644 Mage.Client/src/main/resources/flags/be.png create mode 100644 Mage.Client/src/main/resources/flags/bf.png create mode 100644 Mage.Client/src/main/resources/flags/bg.png create mode 100644 Mage.Client/src/main/resources/flags/bh.png create mode 100644 Mage.Client/src/main/resources/flags/bi.png create mode 100644 Mage.Client/src/main/resources/flags/bj.png create mode 100644 Mage.Client/src/main/resources/flags/bm.png create mode 100644 Mage.Client/src/main/resources/flags/bn.png create mode 100644 Mage.Client/src/main/resources/flags/bo.png create mode 100644 Mage.Client/src/main/resources/flags/br.png create mode 100644 Mage.Client/src/main/resources/flags/bs.png create mode 100644 Mage.Client/src/main/resources/flags/bt.png create mode 100644 Mage.Client/src/main/resources/flags/bv.png create mode 100644 Mage.Client/src/main/resources/flags/bw.png create mode 100644 Mage.Client/src/main/resources/flags/by.png create mode 100644 Mage.Client/src/main/resources/flags/bz.png create mode 100644 Mage.Client/src/main/resources/flags/ca.png create mode 100644 Mage.Client/src/main/resources/flags/catalonia.png create mode 100644 Mage.Client/src/main/resources/flags/cc.png create mode 100644 Mage.Client/src/main/resources/flags/cd.png create mode 100644 Mage.Client/src/main/resources/flags/cf.png create mode 100644 Mage.Client/src/main/resources/flags/cg.png create mode 100644 Mage.Client/src/main/resources/flags/ch.png create mode 100644 Mage.Client/src/main/resources/flags/ci.png create mode 100644 Mage.Client/src/main/resources/flags/ck.png create mode 100644 Mage.Client/src/main/resources/flags/cl.png create mode 100644 Mage.Client/src/main/resources/flags/cm.png create mode 100644 Mage.Client/src/main/resources/flags/cn.png create mode 100644 Mage.Client/src/main/resources/flags/co.png create mode 100644 Mage.Client/src/main/resources/flags/cr.png create mode 100644 Mage.Client/src/main/resources/flags/cs.png create mode 100644 Mage.Client/src/main/resources/flags/cu.png create mode 100644 Mage.Client/src/main/resources/flags/cv.png create mode 100644 Mage.Client/src/main/resources/flags/cx.png create mode 100644 Mage.Client/src/main/resources/flags/cy.png create mode 100644 Mage.Client/src/main/resources/flags/cz.png create mode 100644 Mage.Client/src/main/resources/flags/de.png create mode 100644 Mage.Client/src/main/resources/flags/dj.png create mode 100644 Mage.Client/src/main/resources/flags/dk.png create mode 100644 Mage.Client/src/main/resources/flags/dm.png create mode 100644 Mage.Client/src/main/resources/flags/do.png create mode 100644 Mage.Client/src/main/resources/flags/dz.png create mode 100644 Mage.Client/src/main/resources/flags/ec.png create mode 100644 Mage.Client/src/main/resources/flags/ee.png create mode 100644 Mage.Client/src/main/resources/flags/eg.png create mode 100644 Mage.Client/src/main/resources/flags/eh.png create mode 100644 Mage.Client/src/main/resources/flags/england.png create mode 100644 Mage.Client/src/main/resources/flags/er.png create mode 100644 Mage.Client/src/main/resources/flags/es.png create mode 100644 Mage.Client/src/main/resources/flags/et.png create mode 100644 Mage.Client/src/main/resources/flags/europeanunion.png create mode 100644 Mage.Client/src/main/resources/flags/fi.png create mode 100644 Mage.Client/src/main/resources/flags/fj.png create mode 100644 Mage.Client/src/main/resources/flags/fk.png create mode 100644 Mage.Client/src/main/resources/flags/fm.png create mode 100644 Mage.Client/src/main/resources/flags/fo.png create mode 100644 Mage.Client/src/main/resources/flags/fr.png create mode 100644 Mage.Client/src/main/resources/flags/ga.png create mode 100644 Mage.Client/src/main/resources/flags/gb.png create mode 100644 Mage.Client/src/main/resources/flags/gd.png create mode 100644 Mage.Client/src/main/resources/flags/ge.png create mode 100644 Mage.Client/src/main/resources/flags/gf.png create mode 100644 Mage.Client/src/main/resources/flags/gh.png create mode 100644 Mage.Client/src/main/resources/flags/gi.png create mode 100644 Mage.Client/src/main/resources/flags/gl.png create mode 100644 Mage.Client/src/main/resources/flags/gm.png create mode 100644 Mage.Client/src/main/resources/flags/gn.png create mode 100644 Mage.Client/src/main/resources/flags/gp.png create mode 100644 Mage.Client/src/main/resources/flags/gq.png create mode 100644 Mage.Client/src/main/resources/flags/gr.png create mode 100644 Mage.Client/src/main/resources/flags/gs.png create mode 100644 Mage.Client/src/main/resources/flags/gt.png create mode 100644 Mage.Client/src/main/resources/flags/gu.png create mode 100644 Mage.Client/src/main/resources/flags/gw.png create mode 100644 Mage.Client/src/main/resources/flags/gy.png create mode 100644 Mage.Client/src/main/resources/flags/hk.png create mode 100644 Mage.Client/src/main/resources/flags/hm.png create mode 100644 Mage.Client/src/main/resources/flags/hn.png create mode 100644 Mage.Client/src/main/resources/flags/hr.png create mode 100644 Mage.Client/src/main/resources/flags/ht.png create mode 100644 Mage.Client/src/main/resources/flags/hu.png create mode 100644 Mage.Client/src/main/resources/flags/id.png create mode 100644 Mage.Client/src/main/resources/flags/ie.png create mode 100644 Mage.Client/src/main/resources/flags/il.png create mode 100644 Mage.Client/src/main/resources/flags/in.png create mode 100644 Mage.Client/src/main/resources/flags/io.png create mode 100644 Mage.Client/src/main/resources/flags/iq.png create mode 100644 Mage.Client/src/main/resources/flags/ir.png create mode 100644 Mage.Client/src/main/resources/flags/is.png create mode 100644 Mage.Client/src/main/resources/flags/it.png create mode 100644 Mage.Client/src/main/resources/flags/jm.png create mode 100644 Mage.Client/src/main/resources/flags/jo.png create mode 100644 Mage.Client/src/main/resources/flags/jp.png create mode 100644 Mage.Client/src/main/resources/flags/ke.png create mode 100644 Mage.Client/src/main/resources/flags/kg.png create mode 100644 Mage.Client/src/main/resources/flags/kh.png create mode 100644 Mage.Client/src/main/resources/flags/ki.png create mode 100644 Mage.Client/src/main/resources/flags/km.png create mode 100644 Mage.Client/src/main/resources/flags/kn.png create mode 100644 Mage.Client/src/main/resources/flags/kp.png create mode 100644 Mage.Client/src/main/resources/flags/kr.png create mode 100644 Mage.Client/src/main/resources/flags/kw.png create mode 100644 Mage.Client/src/main/resources/flags/ky.png create mode 100644 Mage.Client/src/main/resources/flags/kz.png create mode 100644 Mage.Client/src/main/resources/flags/la.png create mode 100644 Mage.Client/src/main/resources/flags/lb.png create mode 100644 Mage.Client/src/main/resources/flags/lc.png create mode 100644 Mage.Client/src/main/resources/flags/li.png create mode 100644 Mage.Client/src/main/resources/flags/lk.png create mode 100644 Mage.Client/src/main/resources/flags/lr.png create mode 100644 Mage.Client/src/main/resources/flags/ls.png create mode 100644 Mage.Client/src/main/resources/flags/lt.png create mode 100644 Mage.Client/src/main/resources/flags/lu.png create mode 100644 Mage.Client/src/main/resources/flags/lv.png create mode 100644 Mage.Client/src/main/resources/flags/ly.png create mode 100644 Mage.Client/src/main/resources/flags/ma.png create mode 100644 Mage.Client/src/main/resources/flags/mc.png create mode 100644 Mage.Client/src/main/resources/flags/md.png create mode 100644 Mage.Client/src/main/resources/flags/me.png create mode 100644 Mage.Client/src/main/resources/flags/mg.png create mode 100644 Mage.Client/src/main/resources/flags/mh.png create mode 100644 Mage.Client/src/main/resources/flags/mk.png create mode 100644 Mage.Client/src/main/resources/flags/ml.png create mode 100644 Mage.Client/src/main/resources/flags/mm.png create mode 100644 Mage.Client/src/main/resources/flags/mn.png create mode 100644 Mage.Client/src/main/resources/flags/mo.png create mode 100644 Mage.Client/src/main/resources/flags/mp.png create mode 100644 Mage.Client/src/main/resources/flags/mq.png create mode 100644 Mage.Client/src/main/resources/flags/mr.png create mode 100644 Mage.Client/src/main/resources/flags/ms.png create mode 100644 Mage.Client/src/main/resources/flags/mt.png create mode 100644 Mage.Client/src/main/resources/flags/mu.png create mode 100644 Mage.Client/src/main/resources/flags/mv.png create mode 100644 Mage.Client/src/main/resources/flags/mw.png create mode 100644 Mage.Client/src/main/resources/flags/mx.png create mode 100644 Mage.Client/src/main/resources/flags/my.png create mode 100644 Mage.Client/src/main/resources/flags/mz.png create mode 100644 Mage.Client/src/main/resources/flags/na.png create mode 100644 Mage.Client/src/main/resources/flags/nc.png create mode 100644 Mage.Client/src/main/resources/flags/ne.png create mode 100644 Mage.Client/src/main/resources/flags/nf.png create mode 100644 Mage.Client/src/main/resources/flags/ng.png create mode 100644 Mage.Client/src/main/resources/flags/ni.png create mode 100644 Mage.Client/src/main/resources/flags/nl.png create mode 100644 Mage.Client/src/main/resources/flags/no.png create mode 100644 Mage.Client/src/main/resources/flags/np.png create mode 100644 Mage.Client/src/main/resources/flags/nr.png create mode 100644 Mage.Client/src/main/resources/flags/nu.png create mode 100644 Mage.Client/src/main/resources/flags/nz.png create mode 100644 Mage.Client/src/main/resources/flags/om.png create mode 100644 Mage.Client/src/main/resources/flags/pa.png create mode 100644 Mage.Client/src/main/resources/flags/pe.png create mode 100644 Mage.Client/src/main/resources/flags/pf.png create mode 100644 Mage.Client/src/main/resources/flags/pg.png create mode 100644 Mage.Client/src/main/resources/flags/ph.png create mode 100644 Mage.Client/src/main/resources/flags/pk.png create mode 100644 Mage.Client/src/main/resources/flags/pl.png create mode 100644 Mage.Client/src/main/resources/flags/pm.png create mode 100644 Mage.Client/src/main/resources/flags/pn.png create mode 100644 Mage.Client/src/main/resources/flags/pr.png create mode 100644 Mage.Client/src/main/resources/flags/ps.png create mode 100644 Mage.Client/src/main/resources/flags/pt.png create mode 100644 Mage.Client/src/main/resources/flags/pw.png create mode 100644 Mage.Client/src/main/resources/flags/py.png create mode 100644 Mage.Client/src/main/resources/flags/qa.png create mode 100644 Mage.Client/src/main/resources/flags/re.png create mode 100644 Mage.Client/src/main/resources/flags/ro.png create mode 100644 Mage.Client/src/main/resources/flags/rs.png create mode 100644 Mage.Client/src/main/resources/flags/ru.png create mode 100644 Mage.Client/src/main/resources/flags/rw.png create mode 100644 Mage.Client/src/main/resources/flags/sa.png create mode 100644 Mage.Client/src/main/resources/flags/sb.png create mode 100644 Mage.Client/src/main/resources/flags/sc.png create mode 100644 Mage.Client/src/main/resources/flags/scotland.png create mode 100644 Mage.Client/src/main/resources/flags/sd.png create mode 100644 Mage.Client/src/main/resources/flags/se.png create mode 100644 Mage.Client/src/main/resources/flags/sg.png create mode 100644 Mage.Client/src/main/resources/flags/sh.png create mode 100644 Mage.Client/src/main/resources/flags/si.png create mode 100644 Mage.Client/src/main/resources/flags/sj.png create mode 100644 Mage.Client/src/main/resources/flags/sk.png create mode 100644 Mage.Client/src/main/resources/flags/sl.png create mode 100644 Mage.Client/src/main/resources/flags/sm.png create mode 100644 Mage.Client/src/main/resources/flags/sn.png create mode 100644 Mage.Client/src/main/resources/flags/so.png create mode 100644 Mage.Client/src/main/resources/flags/sr.png create mode 100644 Mage.Client/src/main/resources/flags/st.png create mode 100644 Mage.Client/src/main/resources/flags/sv.png create mode 100644 Mage.Client/src/main/resources/flags/sy.png create mode 100644 Mage.Client/src/main/resources/flags/sz.png create mode 100644 Mage.Client/src/main/resources/flags/tc.png create mode 100644 Mage.Client/src/main/resources/flags/td.png create mode 100644 Mage.Client/src/main/resources/flags/tf.png create mode 100644 Mage.Client/src/main/resources/flags/tg.png create mode 100644 Mage.Client/src/main/resources/flags/th.png create mode 100644 Mage.Client/src/main/resources/flags/tj.png create mode 100644 Mage.Client/src/main/resources/flags/tk.png create mode 100644 Mage.Client/src/main/resources/flags/tl.png create mode 100644 Mage.Client/src/main/resources/flags/tm.png create mode 100644 Mage.Client/src/main/resources/flags/tn.png create mode 100644 Mage.Client/src/main/resources/flags/to.png create mode 100644 Mage.Client/src/main/resources/flags/tr.png create mode 100644 Mage.Client/src/main/resources/flags/tt.png create mode 100644 Mage.Client/src/main/resources/flags/tv.png create mode 100644 Mage.Client/src/main/resources/flags/tw.png create mode 100644 Mage.Client/src/main/resources/flags/tz.png create mode 100644 Mage.Client/src/main/resources/flags/ua.png create mode 100644 Mage.Client/src/main/resources/flags/ug.png create mode 100644 Mage.Client/src/main/resources/flags/um.png create mode 100644 Mage.Client/src/main/resources/flags/us.png create mode 100644 Mage.Client/src/main/resources/flags/uy.png create mode 100644 Mage.Client/src/main/resources/flags/uz.png create mode 100644 Mage.Client/src/main/resources/flags/va.png create mode 100644 Mage.Client/src/main/resources/flags/vc.png create mode 100644 Mage.Client/src/main/resources/flags/ve.png create mode 100644 Mage.Client/src/main/resources/flags/vg.png create mode 100644 Mage.Client/src/main/resources/flags/vi.png create mode 100644 Mage.Client/src/main/resources/flags/vn.png create mode 100644 Mage.Client/src/main/resources/flags/vu.png create mode 100644 Mage.Client/src/main/resources/flags/wales.png create mode 100644 Mage.Client/src/main/resources/flags/wf.png create mode 100644 Mage.Client/src/main/resources/flags/world.png create mode 100644 Mage.Client/src/main/resources/flags/ws.png create mode 100644 Mage.Client/src/main/resources/flags/ye.png create mode 100644 Mage.Client/src/main/resources/flags/yt.png create mode 100644 Mage.Client/src/main/resources/flags/za.png create mode 100644 Mage.Client/src/main/resources/flags/zm.png create mode 100644 Mage.Client/src/main/resources/flags/zw.png diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 785715924c4..8a9e3a95f50 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -92,6 +92,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.prefs.Preferences; +import static mage.client.dialog.PreferencesDialog.KEY_CONNECT_FLAG; import mage.view.UserRequestMessage; import net.java.truevfs.access.TArchiveDetector; import net.java.truevfs.access.TConfig; @@ -710,10 +711,6 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { ProxyType proxyType = ProxyType.valueByText(prefs.get("proxyType", "None")); String proxyUsername = prefs.get("proxyUsername", ""); String proxyPassword = prefs.get("proxyPassword", ""); - int avatarId = PreferencesDialog.getSelectedAvatar(); - boolean showAbilityPickerForced = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_ABILITY_PICKER_FORCED, "true").equals("true"); - - try { setCursor(new Cursor(Cursor.WAIT_CURSOR)); Connection connection = new Connection(); @@ -725,10 +722,9 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { connection.setProxyPort(proxyPort); connection.setProxyUsername(proxyUsername); connection.setProxyPassword(proxyPassword); - connection.setAvatarId(avatarId); - connection.setShowAbilityPickerForced(showAbilityPickerForced); - connection.setAllowRequestShowHandCards(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true").equals("true")); - connection.setUserSkipPrioritySteps(PreferencesDialog.getUserSkipPrioritySteps()); + + setUserPrefsToConnection(connection); + logger.debug("connecting (auto): " + proxyType + " " + proxyServer + " " + proxyPort + " " + proxyUsername); if (MageFrame.connect(connection)) { showGames(false); @@ -742,6 +738,17 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { return false; } + public void setUserPrefsToConnection(Connection connection) { + int avatarId = PreferencesDialog.getSelectedAvatar(); + connection.setAvatarId(avatarId); + boolean showAbilityPickerForced = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_ABILITY_PICKER_FORCED, "true").equals("true"); + connection.setShowAbilityPickerForced(showAbilityPickerForced); + connection.setAllowRequestShowHandCards(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true").equals("true")); + connection.setConfirmEmptyManaPool(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true").equals("true")); + connection.setUserSkipPrioritySteps(PreferencesDialog.getUserSkipPrioritySteps()); + connection.setFlagName(MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world.png")); + } + /** * This method is called from within the constructor to * initialize the form. diff --git a/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java b/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java index c078527ebd1..ff2955e0fec 100644 --- a/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java +++ b/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java @@ -311,7 +311,7 @@ public class ChatPanel extends javax.swing.JPanel { class TableModel extends AbstractTableModel { - private final String[] columnNames = new String[]{"Players", "Info", "Games", "Connection"}; + private final String[] columnNames = new String[]{" ","Players", "Info", "Games", "Connection"}; private UsersView[] players = new UsersView[0]; public void loadData(Collection roomUserInfoList) throws MageRemoteException { @@ -319,8 +319,9 @@ public class ChatPanel extends javax.swing.JPanel { this.players = roomUserInfo.getUsersView().toArray(new UsersView[0]); JTableHeader th = jTablePlayers.getTableHeader(); TableColumnModel tcm = th.getColumnModel(); - tcm.getColumn(0).setHeaderValue("Players (" + this.players.length + ")"); - tcm.getColumn(2).setHeaderValue( + + tcm.getColumn(1).setHeaderValue("Players (" + this.players.length + ")"); + tcm.getColumn(3).setHeaderValue( "Games " + roomUserInfo.getNumberActiveGames() + (roomUserInfo.getNumberActiveGames() != roomUserInfo.getNumberGameThreads() ? " (T:" + roomUserInfo.getNumberGameThreads():" (") + " limit: " + roomUserInfo.getNumberMaxGames() + ")"); @@ -342,12 +343,14 @@ public class ChatPanel extends javax.swing.JPanel { public Object getValueAt(int arg0, int arg1) { switch (arg1) { case 0: - return players[arg0].getUserName(); + return players[arg0].getFlagName(); case 1: - return players[arg0].getInfoState(); + return players[arg0].getUserName(); case 2: - return players[arg0].getInfoGames(); + return players[arg0].getInfoState(); case 3: + return players[arg0].getInfoGames(); + case 4: return players[arg0].getInfoPing(); } return ""; diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form index 3389484c5d8..e84af7c5e33 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.form @@ -35,20 +35,19 @@ - - - - + + + + + + + - - - - - - + + @@ -57,8 +56,9 @@ - + + @@ -86,12 +86,17 @@ + + + + + - + @@ -142,11 +147,24 @@ - + + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index a92c7a937fb..d26d235edd8 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -37,7 +37,6 @@ package mage.client.dialog; import mage.client.MageFrame; import mage.client.util.Config; import mage.remote.Connection; -import mage.remote.Connection.ProxyType; import org.apache.log4j.Logger; import javax.swing.*; @@ -64,6 +63,9 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import static mage.client.dialog.PreferencesDialog.KEY_CONNECT_AUTO_CONNECT; +import static mage.client.dialog.PreferencesDialog.KEY_CONNECT_FLAG; +import mage.remote.Connection.ProxyType; /** * @author BetaSteward_at_googlemail.com @@ -96,8 +98,18 @@ public class ConnectDialog extends MageDialog { this.txtServer.setText(MageFrame.getPreferences().get("serverAddress", Config.serverName)); this.txtPort.setText(MageFrame.getPreferences().get("serverPort", Integer.toString(Config.port))); this.txtUserName.setText(MageFrame.getPreferences().get("userName", "")); - this.chkAutoConnect.setSelected(Boolean.parseBoolean(MageFrame.getPreferences().get("autoConnect", "false"))); + this.chkAutoConnect.setSelected(Boolean.parseBoolean(MageFrame.getPreferences().get(KEY_CONNECT_AUTO_CONNECT, "false"))); this.chkForceUpdateDB.setSelected(false); // has always to be set manually to force comparison + + String selectedFlag = MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world.png"); + // set the selected country/flag + for (int i = 0; i < cbFlag.getItemCount(); i++) { + String[] name = (String[])cbFlag.getItemAt(i); + if (name[1].equals(selectedFlag)) { + cbFlag.setSelectedIndex(i); + break; + } + } this.setModal(true); this.setLocation(50, 50); this.setVisible(true); @@ -107,7 +119,7 @@ public class ConnectDialog extends MageDialog { MageFrame.getPreferences().put("serverAddress", txtServer.getText().trim()); MageFrame.getPreferences().put("serverPort", txtPort.getText().trim()); MageFrame.getPreferences().put("userName", txtUserName.getText().trim()); - MageFrame.getPreferences().put("autoConnect", Boolean.toString(chkAutoConnect.isSelected())); + MageFrame.getPreferences().put(KEY_CONNECT_AUTO_CONNECT, Boolean.toString(chkAutoConnect.isSelected())); } /** @@ -127,6 +139,8 @@ public class ConnectDialog extends MageDialog { txtPort = new javax.swing.JTextField(); lblUserName = new javax.swing.JLabel(); txtUserName = new javax.swing.JTextField(); + lblFlag = new javax.swing.JLabel(); + cbFlag = new mage.client.util.gui.countryBox.CountryComboBox(); chkAutoConnect = new javax.swing.JCheckBox(); chkForceUpdateDB = new javax.swing.JCheckBox(); jProxySettingsButton = new javax.swing.JButton(); @@ -159,7 +173,12 @@ public class ConnectDialog extends MageDialog { }); lblUserName.setLabelFor(txtUserName); - lblUserName.setText("User Name:"); + lblUserName.setText("User name:"); + + lblFlag.setLabelFor(txtUserName); + lblFlag.setText("User flag:"); + + cbFlag.setEditable(true); chkAutoConnect.setText("Automatically connect to this server next time"); chkAutoConnect.setToolTipText("If active this connect dialog will not be shown if you choose to connect.
\nInstead XMage tries to connect to the last server you were connected to."); @@ -211,26 +230,26 @@ public class ConnectDialog extends MageDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnCancel)) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(lblPort) - .addComponent(lblServer) - .addComponent(lblUserName)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(lblPort) + .addComponent(lblServer) + .addComponent(lblUserName)) + .addComponent(lblFlag, javax.swing.GroupLayout.Alignment.TRAILING)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblStatus, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(chkForceUpdateDB, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(chkAutoConnect, javax.swing.GroupLayout.DEFAULT_SIZE, 362, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addComponent(jProxySettingsButton) - .addGap(164, 237, Short.MAX_VALUE)) + .addComponent(jProxySettingsButton) + .addComponent(cbFlag, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) .addComponent(txtServer, javax.swing.GroupLayout.DEFAULT_SIZE, 286, Short.MAX_VALUE) .addComponent(txtPort, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(txtUserName)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnFind) - .addGap(0, 0, Short.MAX_VALUE))))) + .addComponent(btnFind)) + .addComponent(chkForceUpdateDB, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(chkAutoConnect, javax.swing.GroupLayout.DEFAULT_SIZE, 358, Short.MAX_VALUE)))) .addContainerGap()) ); layout.setVerticalGroup( @@ -250,12 +269,16 @@ public class ConnectDialog extends MageDialog { .addComponent(txtUserName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(lblUserName)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(lblFlag, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(cbFlag, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGap(5, 5, 5) .addComponent(chkAutoConnect) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(chkForceUpdateDB) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jProxySettingsButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 20, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 50, Short.MAX_VALUE) .addComponent(lblStatus, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -269,6 +292,7 @@ public class ConnectDialog extends MageDialog { private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed MageFrame.getPreferences().put("autoConnect", Boolean.toString(chkAutoConnect.isSelected())); + MageFrame.getPreferences().put(KEY_CONNECT_FLAG, (String)cbFlag.getSelectedItem()); if (task != null && !task.isDone()) { task.cancel(true); } else { @@ -304,7 +328,7 @@ public class ConnectDialog extends MageDialog { connection.setPort(Integer.valueOf(this.txtPort.getText().trim())); connection.setUsername(this.txtUserName.getText().trim()); connection.setForceDBComparison(this.chkForceUpdateDB.isSelected()); - + MageFrame.getPreferences().put(KEY_CONNECT_FLAG, (String)cbFlag.getSelectedItem()); ProxyType configProxyType = Connection.ProxyType.valueByText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_TYPE, "None")); @@ -329,13 +353,8 @@ public class ConnectDialog extends MageDialog { } // pref settings - int avatarId = PreferencesDialog.getSelectedAvatar(); - connection.setAvatarId(avatarId); - boolean showAbilityPickerForced = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_ABILITY_PICKER_FORCED, "true").equals("true"); - connection.setAllowRequestShowHandCards(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true").equals("true")); - connection.setShowAbilityPickerForced(showAbilityPickerForced); - connection.setUserSkipPrioritySteps(PreferencesDialog.getUserSkipPrioritySteps()); - connection.setConfirmEmptyManaPool(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true").equals("true")); + MageFrame.getInstance().setUserPrefsToConnection(connection); + logger.debug("connecting: " + connection.getProxyType() + " " + connection.getProxyHost() + " " + connection.getProxyPort()); task = new ConnectTask(); task.execute(); @@ -524,9 +543,11 @@ public class ConnectDialog extends MageDialog { private javax.swing.JButton btnCancel; private javax.swing.JButton btnConnect; private javax.swing.JButton btnFind; + private mage.client.util.gui.countryBox.CountryComboBox cbFlag; private javax.swing.JCheckBox chkAutoConnect; private javax.swing.JCheckBox chkForceUpdateDB; private javax.swing.JButton jProxySettingsButton; + private javax.swing.JLabel lblFlag; private javax.swing.JLabel lblPort; private javax.swing.JLabel lblServer; private javax.swing.JLabel lblStatus; diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form index 3a64ad1f596..06253718466 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.form @@ -31,7 +31,7 @@ - +
diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index c1775a462ac..e33b9c0b92d 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -226,6 +226,10 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_CONNECTION_URL_SERVER_LIST = "connectionURLServerList"; public static final String KEY_AVATAR = "selectedId"; + + public static final String KEY_CONNECT_AUTO_CONNECT = "autoConnect"; + public static final String KEY_CONNECT_FLAG = "connectFlag"; + private static final Map cache = new HashMap<>(); @@ -1713,7 +1717,8 @@ public class PreferencesDialog extends javax.swing.JDialog { dialog.showAbilityPickerForced.isSelected(), dialog.cbAllowRequestToShowHandCards.isSelected(), dialog.cbConfirmEmptyManaPool.isSelected(), - getUserSkipPrioritySteps()); + getUserSkipPrioritySteps(), + MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world.png")); prefs.flush(); } catch (BackingStoreException ex) { @@ -2390,7 +2395,9 @@ public class PreferencesDialog extends javax.swing.JDialog { PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_TOOLTIPS_ANY_ZONE, "true").equals("true"), PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_ALLOW_REQUEST_SHOW_HAND_CARDS, "true").equals("true"), PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GAME_CONFIRM_EMPTY_MANA_POOL, "true").equals("true"), - getUserSkipPrioritySteps()); + getUserSkipPrioritySteps(), + MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world.png") + ); } } }); diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryComboBox.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryComboBox.java new file mode 100644 index 00000000000..2c365d95e3c --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryComboBox.java @@ -0,0 +1,315 @@ +/* + * 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 mage.client.util.gui.countryBox; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; + +/** + * A custom combo box with its own renderer and editor. + * @author wwww.codejava.net + * + */ +public class CountryComboBox extends JComboBox { + + private final DefaultComboBoxModel model; + + public static String[][] countryList = { + {"Afghanistan", "af.png"}, + {"Åland Islands", "ax.png"}, + {"Albania", "al.png"}, + {"Algeria", "dz.png"}, + {"American Samoa", "as.png"}, + {"Andorra", "ad.png"}, + {"Angola", "an.png"}, + {"Anguilla", "ai.png"}, + {"Antarctica", "ao.png"}, + {"Antigua and Barbuda", "ag.png"}, + {"Argentina", "ar.png"}, + {"Armenia", "am.png"}, + {"Aruba", "aw.png"}, + {"Australia", "au.png"}, + {"Austria", "at.png"}, + {"Azerbaijan", "az.png"}, + {"Bahamas", "bs.png"}, + {"Bahrain", "bh.png"}, + {"Bangladesh", "bd.png"}, + {"Barbados", "bb.png"}, + {"Belarus", "by.png"}, + {"Belgium", "be.png"}, + {"Belize", "bz.png"}, + {"Benin", "bj.png"}, + {"Bermuda", "bm.png"}, + {"Bhutan", "bt.png"}, + {"Bolivia, Plurinational State of", "bo.png"}, + {"Bosnia and Herzegovina", "ba.png"}, + {"Botswana", "bw.png"}, + {"Bouvet Island", "bv.png"}, + {"Brazil", "br.png"}, + {"British Indian Ocean Territory", "io.png"}, + {"Brunei Darussalam", "bn.png"}, + {"Bulgaria", "bg.png"}, + {"Burkina Faso", "bf.png"}, + {"Burundi", "bi.png"}, + {"Cabo Verde", "cv.png"}, + {"Cambodia", "kh.png"}, + {"Cameroon", "cm.png"}, + {"Canada", "ca.png"}, + {"Catalonia", "catalonia.png"}, + {"Cayman Islands", "ky.png"}, + {"Central African Republic", "cf.png"}, + {"Chad", "td.png"}, + {"Chile", "cl.png"}, + {"China", "cn.png"}, + {"Christmas Island", "cx.png"}, + {"Cocos (Keeling) Islands", "cc.png"}, + {"Colombia", "co.png"}, + {"Comoros", "km.png"}, + {"Congo", "cg.png"}, + {"Congo, the Democratic Republic of the", "cd.png"}, + {"Cook Islands", "ck.png"}, + {"Costa Rica", "cr.png"}, + {"Côte d'Ivoire", "ci.png"}, + {"Croatia", "hr.png"}, + {"Cuba", "cu.png"}, + {"Cyprus", "cy.png"}, + {"Czech Republic", "cz.png"}, + {"Denmark", "dk.png"}, + {"Djibouti", "dj.png"}, + {"Dominica", "dm.png"}, + {"Dominican Republic", "do.png"}, + {"Ecuador", "ec.png"}, + {"Egypt", "eg.png"}, + {"El Salvador", "sv.png"}, + {"England", "england.png"}, + {"Equatorial Guinea", "gq.png"}, + {"Eritrea", "er.png"}, + {"Estonia", "ee.png"}, + {"Ethiopia", "et.png"}, + {"European Union", "europeanunion.png"}, + {"Falkland Islands (Malvinas)", "fk.png"}, + {"Faroe Islands", "fo.png"}, + {"Fiji", "fj.png"}, + {"Finland", "fi.png"}, + {"France", "fr.png"}, + {"French Guiana", "gf.png"}, + {"French Polynesia", "pf.png"}, + {"French Southern Territories", "tf.png"}, + {"Gabon", "ga.png"}, + {"Gambia", "gm.png"}, + {"Georgia", "ge.png"}, + {"Germany", "de.png"}, + {"Ghana", "gh.png"}, + {"Gibraltar", "gi.png"}, + {"Greece", "gr.png"}, + {"Greenland", "gl.png"}, + {"Grenada", "gd.png"}, + {"Guadeloupe", "gp.png"}, + {"Guam", "gu.png"}, + {"Guatemala", "gt.png"}, + {"Guinea", "gn.png"}, + {"Guinea-Bissau", "gw.png"}, + {"Guyana", "gy.png"}, + {"Haiti", "ht.png"}, + {"Heard Island and McDonald Islands", "hm.png"}, + {"Holy See", "va.png"}, + {"Honduras", "hn.png"}, + {"Hong Kong", "hk.png"}, + {"Hungary", "hu.png"}, + {"Iceland", "is.png"}, + {"India", "in.png"}, + {"Indonesia", "id.png"}, + {"Iran, Islamic Republic of", "ir.png"}, + {"Iraq", "iq.png"}, + {"Ireland", "ie.png"}, + {"Israel", "il.png"}, + {"Italy", "it.png"}, + {"Jamaica", "jm.png"}, + {"Japan", "jp.png"}, + {"Jordan", "jo.png"}, + {"Kazakhstan", "kz.png"}, + {"Kenya", "ke.png"}, + {"Kiribati", "ki.png"}, + {"Korea, Democratic People's Republic of", "kp.png"}, + {"Korea, Republic of", "kr.png"}, + {"Kuwait", "kw.png"}, + {"Kyrgyzstan", "kg.png"}, + {"Lao People's Democratic Republic", "la.png"}, + {"Latvia", "lv.png"}, + {"Lebanon", "lb.png"}, + {"Lesotho", "ls.png"}, + {"Liberia", "lr.png"}, + {"Libya", "ly.png"}, + {"Liechtenstein", "li.png"}, + {"Lithuania", "lt.png"}, + {"Luxembourg", "lu.png"}, + {"Macao", "mo.png"}, + {"Macedonia, the former Yugoslav Republic of", "mk.png"}, + {"Madagascar", "mg.png"}, + {"Malawi", "mw.png"}, + {"Malaysia", "my.png"}, + {"Maldives", "mv.png"}, + {"Mali", "ml.png"}, + {"Malta", "mt.png"}, + {"Marshall Islands", "mh.png"}, + {"Martinique", "mq.png"}, + {"Mauritania", "mr.png"}, + {"Mauritius", "mu.png"}, + {"Mayotte", "yt.png"}, + {"Mexico", "mx.png"}, + {"Micronesia, Federated States of", "fm.png"}, + {"Moldova, Republic of", "md.png"}, + {"Monaco", "mc.png"}, + {"Mongolia", "mn.png"}, + {"Montenegro", "me.png"}, + {"Montserrat", "ms.png"}, + {"Morocco", "ma.png"}, + {"Mozambique", "mz.png"}, + {"Myanmar", "mm.png"}, + {"Namibia", "na.png"}, + {"Nauru", "nr.png"}, + {"Nepal", "np.png"}, + {"Netherlands", "nl.png"}, + {"New Caledonia", "nc.png"}, + {"New Zealand", "nz.png"}, + {"Nicaragua", "ni.png"}, + {"Niger", "ne.png"}, + {"Nigeria", "ng.png"}, + {"Niue", "nu.png"}, + {"Norfolk Island", "nf.png"}, + {"Northern Mariana Islands", "mp.png"}, + {"Norway", "no.png"}, + {"Oman", "om.png"}, + {"Pakistan", "pk.png"}, + {"Palau", "pw.png"}, + {"Palestine, State of", "ps.png"}, + {"Panama", "pa.png"}, + {"Papua New Guinea", "pg.png"}, + {"Paraguay", "py.png"}, + {"Peru", "pe.png"}, + {"Philippines", "ph.png"}, + {"Pitcairn", "pn.png"}, + {"Poland", "pl.png"}, + {"Portugal", "pt.png"}, + {"Puerto Rico", "pr.png"}, + {"Qatar", "qa.png"}, + {"Réunion", "re.png"}, + {"Romania", "ro.png"}, + {"Russian Federation", "ru.png"}, + {"Rwanda", "rw.png"}, + {"Saint Helena, Ascension and Tristan da Cunha", "sh.png"}, + {"Saint Kitts and Nevis", "kn.png"}, + {"Saint Lucia", "lc.png"}, + {"Saint Pierre and Miquelon", "pm.png"}, + {"Saint Vincent and the Grenadines", "vc.png"}, + {"Samoa", "ws.png"}, + {"San Marino", "sm.png"}, + {"Sao Tome and Principe", "st.png"}, + {"Saudi Arabia", "sa.png"}, + {"Scotland", "scotland.png"}, + {"Senegal", "sn.png"}, + {"Serbia", "rs.png"}, + {"Seychelles", "sc.png"}, + {"Sierra Leone", "sl.png"}, + {"Singapore", "sg.png"}, + {"Slovakia", "sk.png"}, + {"Slovenia", "si.png"}, + {"Solomon Islands", "sb.png"}, + {"Somalia", "so.png"}, + {"South Africa", "za.png"}, + {"South Georgia and the South Sandwich Islands", "gs.png"}, + {"Spain", "es.png"}, + {"Sri Lanka", "lk.png"}, + {"Sudan", "sd.png"}, + {"Suriname", "sr.png"}, + {"Svalbard and Jan Mayen", "sj.png"}, + {"Swaziland", "sz.png"}, + {"Sweden", "se.png"}, + {"Switzerland", "ch.png"}, + {"Syrian Arab Republic", "sy.png"}, + {"Taiwan, Province of China", "tw.png"}, + {"Tajikistan", "tj.png"}, + {"Tanzania, United Republic of", "tz.png"}, + {"Thailand", "th.png"}, + {"Timor-Leste", "tl.png"}, + {"Togo", "tg.png"}, + {"Tokelau", "tk.png"}, + {"Tonga", "to.png"}, + {"Trinidad and Tobago", "tt.png"}, + {"Tunisia", "tn.png"}, + {"Turkey", "tr.png"}, + {"Turkmenistan", "tm.png"}, + {"Turks and Caicos Islands", "tc.png"}, + {"Tuvalu", "tv.png"}, + {"Uganda", "ug.png"}, + {"Ukraine", "ua.png"}, + {"United Arab Emirates", "ae.png"}, + {"United Kingdom of Great Britain and Northern Ireland", "gb.png"}, + {"United States Minor Outlying Islands", "um.png"}, + {"United States of America", "us.png"}, + {"Uruguay", "uy.png"}, + {"Uzbekistan", "uz.png"}, + {"Vanuatu", "vu.png"}, + {"Venezuela, Bolivarian Republic of", "ve.png"}, + {"Viet Nam", "vn.png"}, + {"Virgin Islands, British", "vg.png"}, + {"Virgin Islands, U.S.", "vi.png"}, + {"Wales", "wales.png"}, + {"Wallis and Futuna", "wf.png"}, + {"World", "world.png"}, + {"Western Sahara", "eh.png"}, + {"Yemen", "ye.png"}, + {"Zambia", "zm.png"}, + {"Zimbabwe", "zw.png"}, + }; + + @SuppressWarnings("unchecked") + public CountryComboBox() { + model = new DefaultComboBoxModel(); + setModel(model); + setRenderer(new CountryItemRenderer()); + setEditor(new CountryItemEditor()); + addItems(countryList); + } + + /** + * Add an array items to this combo box. + * Each item is an array of two String elements: + * - first element is country name. + * - second element is path of an image file for country flag. + * @param items + */ + @SuppressWarnings("unchecked") + public void addItems(String[][] items) { + for (String[] anItem : items) { + model.addElement(anItem); + } + } + + +} diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemEditor.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemEditor.java new file mode 100644 index 00000000000..11d6e66cc91 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemEditor.java @@ -0,0 +1,87 @@ +/* + * 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 mage.client.util.gui.countryBox; + +import java.awt.Color; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.plaf.basic.BasicComboBoxEditor; + + +/** + * Editor for JComboBox + * @author wwww.codejava.net + * + */ +public class CountryItemEditor extends BasicComboBoxEditor { + private final JPanel panel = new JPanel(); + private final JLabel labelItem = new JLabel(); + private String selectedValue; + + public CountryItemEditor() { + panel.setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.weightx = 1.0; + constraints.insets = new Insets(2, 5, 2, 2); + + labelItem.setOpaque(false); + labelItem.setHorizontalAlignment(JLabel.LEFT); + labelItem.setForeground(Color.WHITE); + + panel.add(labelItem, constraints); + panel.setBackground(new Color(0,104,139, 0)); + selectedValue = null; + } + + @Override + public Component getEditorComponent() { + return this.panel; + } + + @Override + public Object getItem() { + return this.selectedValue; + } + + @Override + public void setItem(Object item) { + if (item == null || !(item instanceof String[])) { + return; + } + String[] countryItem = (String[]) item; + selectedValue = countryItem[0]; + labelItem.setText(selectedValue); + labelItem.setIcon(new ImageIcon(getClass().getResource("/flags/"+ countryItem[1]))); + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemRenderer.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemRenderer.java new file mode 100644 index 00000000000..38bfaa4867c --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemRenderer.java @@ -0,0 +1,87 @@ +/* + * 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 mage.client.util.gui.countryBox; + +import java.awt.Color; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; + + +/** + * Customer renderer for JComboBox + * @author www.codejava.net + * + */ + +public class CountryItemRenderer extends JPanel implements ListCellRenderer { + private final JLabel labelItem = new JLabel(); + + public CountryItemRenderer() { + setLayout(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraints(); + constraints.fill = GridBagConstraints.HORIZONTAL; + constraints.weightx = 1.0; + constraints.insets = new Insets(0, 0, 0, 0); + + labelItem.setOpaque(true); + labelItem.setHorizontalAlignment(JLabel.LEFT); + + add(labelItem, constraints); + setBackground(Color.LIGHT_GRAY); + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + String[] countryItem = (String[]) value; + + // set country name + labelItem.setText(countryItem[0]); + + // set country flag + labelItem.setIcon(new ImageIcon(countryItem[1])); + + if (isSelected) { + labelItem.setBackground(Color.BLUE); + labelItem.setForeground(Color.YELLOW); + } else { + labelItem.setForeground(Color.BLACK); + labelItem.setBackground(Color.LIGHT_GRAY); + } + + return this; + } + +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxEditor.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxEditor.java new file mode 100644 index 00000000000..6c8db0b4723 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxEditor.java @@ -0,0 +1,77 @@ +/* + * 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 mage.client.util.gui.countryBox; + +import java.awt.Color; +import java.awt.Component; +import java.awt.FlowLayout; +import java.awt.Font; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.plaf.basic.BasicComboBoxEditor; + +/** + * + * @author LevelX2 + */ + +public class MyComboBoxEditor extends BasicComboBoxEditor { + private final JLabel label; + private final JPanel panel; + private Object selectedItem; + + public MyComboBoxEditor() { + label = new JLabel(); + panel = new JPanel(); + + label.setOpaque(false); + label.setFont(new Font("Arial", Font.BOLD, 14)); + label.setForeground(Color.BLACK); + + panel.setLayout(new FlowLayout(FlowLayout.LEFT, 5, 2)); + panel.add(label); + panel.setBackground(Color.BLUE); + } + + @Override + public Component getEditorComponent() { + return this.panel; + } + + @Override + public Object getItem() { + return "[" + this.selectedItem.toString() + "]"; + } + + @Override + public void setItem(Object item) { + this.selectedItem = item; + label.setText(item.toString()); + } + +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxRenderer.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxRenderer.java new file mode 100644 index 00000000000..d4c755dfca7 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/MyComboBoxRenderer.java @@ -0,0 +1,57 @@ +/* + * 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 mage.client.util.gui.countryBox; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.ListCellRenderer; + +/** + * + * @author LevelX2 + */ +public class MyComboBoxRenderer extends JLabel implements ListCellRenderer { + + public MyComboBoxRenderer() { + setOpaque(true); + setFont(new Font("Arial", Font.BOLD /*| Font.ITALIC*/, 14)); + setBackground(Color.BLUE); + setForeground(Color.YELLOW); + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, boolean cellHasFocus) { + setText(value.toString()); + return this; + } + +} diff --git a/Mage.Client/src/main/resources/flags/ad.png b/Mage.Client/src/main/resources/flags/ad.png new file mode 100644 index 0000000000000000000000000000000000000000..625ca84f9ec596848d4b967b5556fda897ca7183 GIT binary patch literal 643 zcmV-}0(||6P)S}{rU-#^xW|9%5S{`_G8k=zVG=|5luWB>#JF#yj01oZa& zTu&JQ008~}|NQ&{0055x0sR95`v3p?1_t~7{`v!N`v3d-{`&g=`T6amqXIBA$0h)T zKokWRkpoMx|Go@O4FvhR>O1p+i7`B6t^3)y2dJ<#?4I?d4x-E}Az98Z`2`TmzkmP! z{`(J9{pauh-+zJD{JOYLhW+QSzkh#x`Y&wke*WLjpZ~u9`2XuCr@{^OJ|MkZb_UGR@Z=V>fB*iaq<8P{*B}2u-uMnu#J~U$KrBDL0bT$1_irEqiZcM6WEF7g<|*+% zzkjHi-TwRc=f7_t9|K*?`1?07lmG&V<)V$@{}>pUYZ(~+fDw@M3(WY1!T<=sp}f-o0K!lJ3<>Jc!&VtMslE3ph2^H3FGe(F z4|^}slF1@l1Nxc}^5hjjU=0la|37)k@b@neJ^1|l@87?_{{z(l6@kcKe}Db@_Y267 z<&g#201!YdV6*>2R0EZ*bYA%%2Vi7me5m>mAb?naPGtnD20MY__n-f`0mzg8{s9CK z*hzmtW)s%H$oS{aAAkS?JLxaT`2W9u;n%DD1Q-B%#%TuGpFSS|0000&5HW|5#c7{r(N){s9YCi84$~U|{$Glm>AD0*K|^yLU}ZO+eP) ze;~;4`#0mCzs$dX{r~-&?f370zkUTM{{OB1_1E8DKmYz*JbN)f0I>i8Q1#1Kum1f0 z{pZggpt}FRe*ORX>;JEx|9}4c_w(m__W!pfKK=atLxM{ZXbC_7vHbh@@9*EgK-ECy zzkh)Y{?EYhFEQc&-#`EN9{7Lq7*OE*@9)2U|NQmmC(vO40VtezBLF}U2%?OI(TG)& zJC>!~;SYmN-)zb6gc(7A-^Q(#z0vHb9!CJ#T#ii{@&pjek8j`pfX)5|H00+GpnLvt za{MtfWBC4qk%R63j~{=2{QLgr`wyTb*amx2hce{;{QL3a|BhWh#6A5bKmf5of*%NgdVm^$Zuo!gI_LQd|G=96g51yWl0Agf71rLE? q3XWqq@&Lw?Iyk0*94-cc00RK~MQKxxUU$F%0000= z`}|7!`YQYT5C8xIh~*yx1DgQ1_jkSr_QgN{Gn)!7=zkRT=l(yS{y%^Jd$>%yrP!)vVq~xpZ~xA{{HjZ>hB|)Z~OQoBLBm{yCqA0{k;C`&+mV~|1$jj15^wU zKukakbkF}kzkmk({q_6zmUTZaUWBOraqa5!?(V-pgMR}R{04?0&`AIR#PSE^hrdi> z;*7t4gLVFSJ$J5DTibsorq@%ai5h7B2l^1G@*gM=fUW@uAjYc<48V{DX@ijvE(11_ dg@FMezyQzuBHsGv+i3s*002ovPDHLkV1jTADB=JB literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ai.png b/Mage.Client/src/main/resources/flags/ai.png new file mode 100644 index 0000000000000000000000000000000000000000..74ed29d92616c86757d3c0ec04378301c8f591b4 GIT binary patch literal 643 zcmV-}0(||6P)ocPy#thD++(+#W%SS0Qvp^`1%0+_yGC)0QUL-|M&m^ z`T_tk0M7pe&iDc7fh@K74M*k&0{8f*88BG;;GY6ve?;&4{{Q>_{{8&^`u+g@|Ns5} z|C?Nv0*Gb#mu+n4z6ZW~#qjy_|6ji&ZeIEMk16oyzH)i@Pk$MNxLARR?a#kwpMS72 zF)}cG2M8dRUN*~81wCz%$A2$f`_IL|A|w6j^RHtc7{2`lYG-KkQ{(*knSqh%3kzr7 zs%s1kKL7#%F#yj00-^B^D;pD^^AqC)4C(?1m>@q26ceiU4*2{4`u_d~@vQ*ykp&A1 z3h$o${r~^~`vQo?Pm-bY@cm79zVQG5%l!B6)Riap%L`bGemnT<@2|iAfl64!%YWS~ z`p0YY_dig_cYpw5WYAmG*lPLk!;jy8|NZ#~MF0Q%{rCG1ko^Di`(Fhkd3F{?py0c9Vg{{gA~^#>F<|Nj5KapBR^@Bf~CXSi|t;lF=C z8-4)<5DUX^V9+rDCI3RuUy#}V{s46X!}R~}@4ug&fA|+@;n!b(1zCZ900KKMX({{sIILBZE*UQbc|SngJt$+`m9Kz!<;5 dBm+Qz0RWL%NC2%sdjkLf002ovPDHLkV1lARHI@JX literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/al.png b/Mage.Client/src/main/resources/flags/al.png new file mode 100644 index 0000000000000000000000000000000000000000..92354cb6e257be2cade71cb825027ce8d9efc06d GIT binary patch literal 600 zcmV-e0;m0nP)PbXFRCwBA zyv)hK@{fUmi2(>${xJOc&A`II@PmN?BKC`c;SU)710$d~Kmf4-r6niy16BY3`}g0! ze=L9h&Uybegp-Ns@87v^zGQMU|NZla@z1Y+zkdVKmFbHB0*HkH4F0@)&iwE1;&GRLuzkX(OGXMMa``@o$|Ni_2x(y(Jn3#TnHT?el|IhC;U%pHK`}6Df z@2@|9J^lXU=Z~KvfBzi*^!4A*U;lv`e*Fge3Lt=(82$q_`~m9z|ND;$!(WEqze~6o ze}X|JFO$H(U+VvX#{c{avi3L7@c;n?3L&6l7=Hix`}emN<6i~#|7+iUWBL8-&##}m zKKx5zXJr5LA80nvkwCM6egFs{po9Y?& zL6B;olm7ns#=rm&Kp-3b{RIXY(1{??Ha=$NKfkS+|9@us$Mfg+93j@fzkY#y2y!;i z&kO(o1PWH5>zPHwn7|>;0Mz;WPl13iP!Y(WKYy4&4hBXMSPugOKmaiw2gk!dq!|4J mj$lZE0R~eis zC&uN{cDQ8`!@ZNLOqwtoG6nhzPx}SV-d6yzFfcIz75{(rngJyDkKrFw8iaoThHCr$ zo8k9wMhS^O3=9AP!~%5B|9}4(ASQtkLN$m1CV&5705X362095KfLKJhW;OjWdGY7l zzrX*$DuLu*pcFC#NdEr)SL!$SY=)lz0mSm_*B_u)|AM4JP)(}33zy1Lc&_o7? zKL7y)bkd){fBym<_v`mxpbeRs`+WRVot-5A{r$IX$CJFg13u{3eX_{0R++j zR}GCkSy{&4zk!krKoTg(2n-k?g31B}5JW!NMq0d0000q< zM7LZb@rX?|r)2RP-+aO3(7q?c3+$-Vv0E)PxM3SDV%@s`#GkZvw_x^WBa~uq3^l}t zWdf(j(=(>^SgETc#5#EZT(4ObRkfxbzP9G;yza0;Ygc8-_*?EP(ca#`l6-Z6D0{tL zQ4|~MCSi!9Q9YkW=V$ix#EqZ!rc?eWA0TwdVID+3aqrYUXUhCI)Ad5!(cP!BhhW$Ayb2&r8kK!bz*2`~UE W(Ndrek0Jg50000NL7TW{{R0! zJP>i}(MpzaSkj z=eqvqa`^|6{{QbQRQ11q3~Vg_kG}Z}5I`(hw}ncX8D4$|h64jUK>or*=^r$dfI%n0 z$3MKz;!F0w91GjkkV%_X`@4 wKR^hm`X5LwD9*v?4;TUC6Bz$&3;+QJ0QLcJ=WT{TU;qFB07*qoM6N<$f>3bX%K!iX literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/as.png b/Mage.Client/src/main/resources/flags/as.png new file mode 100644 index 0000000000000000000000000000000000000000..32f30e4ce4eedd22d4f09c4f3a46c52dd064f113 GIT binary patch literal 647 zcmV;20(kw2P);Rmc0RI30 z|Nj5|{Q>>`0R8>{{QLs@`vM01^D6+%Ap+Y10Qw3Sr3ED)s=c}b05Jg0{{#dC0RR60 z)z$y}`~dv>0Q&p?0Q>ne0Khr|#}EMN1`-ttJvaFq6VTDg`OMJS*4F}vg#m0fQ1$QM z42u6QTCg3Kt8+Li1J#Y_zUd>4Lw{URe1tEjLbKmY(S0M7pd(g2qsJ0|z;{R`~d z`n<6E*3bb41p*Zr{{R344LkeqyaMyV01O!W0PZ0+TjlEQ0st`p&i?@b0Q{Gi0m{n% z`T6-B``1JQx+4kM0{rp_FfRy7P6Qq%_1|&<`|JPx`Uew8u)+%h2+ zdiAI7xt}_}e|$CmbI9$2>u h*D)|iF)#oG7yty8`q2#I8zBGy002ovPDHLkV1j5VEF}N{ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/at.png b/Mage.Client/src/main/resources/flags/at.png new file mode 100644 index 0000000000000000000000000000000000000000..0f15f34f2883c4b4360fc871d7105309f1533282 GIT binary patch literal 403 zcmV;E0c`$>P)@|4`Xj5kLT%`al?B=W5I`&prl$WjwHQRjfmQ&G0jUOA z@&|Dug_Rm`2Y251|$~)1M2@@6mI}!8O6olw6y^Q z5X--d7nzS8`+x5q12kBmVFD!~j6c5_fMKno0(1^Q0I>i=is|LjXX40RRttS6cG0UZ?;7002ovPDHLkV1fxUnjZiF literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/au.png b/Mage.Client/src/main/resources/flags/au.png new file mode 100644 index 0000000000000000000000000000000000000000..a01389a745d51e16b01a9dc0a707572564a17625 GIT binary patch literal 673 zcmV;S0$%-zP)>fJ3En$GhGS>sbE%%m3$AD)q?8M9y>88-}kR7#RKlk!P~Y_PLuF7~U~3`~nC7 zF#yj00ZUDdpLsm{7ajP|&HwoK0Usg|6%f4L_{`Mi{rvv-`ukf=Ed&Gs-sA7L!Q7*a zj{*QO0M7pb%?Sw^g@yy{>ihEY{`vU@3=8@G0rvO$i3mOL`~mv-`W+b$Mmr&io5dg< z5v!7q0*L95jt`TzK8Kd(Utv)OSp_aLv){6ccV+Z`{Q2+asKUU&aO3`Kpz6wW8wp`< z28M3{0mSqnB#A*-c*8%1=RD#sSOwMznKA3=e&iEzwo{cA=PgXK`2OQ}gqId83!|%* zA_Kz@fB*n70M7pdECCwp4H&@R`1|(w-}M5x*74i)0}%fAt;XafA{48))#>Z>?CD#}*e}Ret0tl$#*RMZ7}Jl7Z|M45`5*URzH9L z{rmSnPy;{!u>dsyO%meg+00000NkvXX Hu0mjfN{&}S literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/aw.png b/Mage.Client/src/main/resources/flags/aw.png new file mode 100644 index 0000000000000000000000000000000000000000..a3579c2d621069c8128d7cf16440d5e45a3ab3cd GIT binary patch literal 524 zcmV+n0`vWeP)^}0h@W9fA9JOn#opKkr#VO<{LPm{QLg}EdH3Vb1Zzpp zqiZ+XNBm%5{`ViKi{bCTe}Dh~2a+HyfB%6Q|9}7eKVjxEfB<6Q<6r}-{{7;G=hB{; zLB+p+|Ni^;|DXT={s75;AoBO$f4@PDf8sps4FCQC1Q64wUw^h*hy4HZXO3U)@85qw zD*pci8U|DWQu_BVi2MUWKoOv;00M{w=;|*lY_o%kegGx^{`&*A{SSok_usF-P)oof z|A86+0*K|;FQAM6fB*FtoEI&bZG5mq328#as{}<$F zWDQ{Ffz1MH001phxCBNdC_2H6U$AI~GQbEJqg*Ti0R{kQ(yH?TRrCY^ O0000^@RCwBA zRL|V|7QjV)*ul;Rlceqc6W0{`~_8AeMg& z|LUXE8U8c=`}dziv6P|mEu%JjM}pqh@65md|Ni~w->=_)fBgZXe?Nc!ot?WGAOHX{ z0M7pe`uG9>{{ZXo`u+d?{`u+u=j#9V2j%Sc^797z|N8p+{rLO+`TP9&`u!FW2mk>3 z0#G;xDFDDQ2vg|)-@5Zw?JVTrAxNs~5&|AZ;5uWfQ`vUsMGm`>aGWoI7=P|oW>%~E z|Kk8dq@aG@HzdsB>fj@shYzX-a%=q<(frSYmfLIv5{HbzQ${~M_P_y6C&m>3uU0*DFd0+8#0#Gk)^|NLQBRAyCBeB9H+Wn{=Mr@*PJ_ZKV< zB>w@GFfafF5Ks>TBP0ZV{bBt5_ve*sKkwd`OiO#eX6=u=cfX!E#rX3NBT(umNC7Yu z0Ro8S7X#4ce?Xr63$phg)Bk64X8r&5``_O`AFf^jc>p5y?+^38KVKLa00M}`wU!~x z=I_1Fe}Df4D*yZI@1I|PfBgnRptt`0`2{iqMpRNPo|?TW>MPOFW(r1_!xlT-yep5|G*@e%^SFxf#DC32@n7=0M7pe00011 zNe=7o{FIuhzQ(sjF~t7>`T+s_0sj00{`>*}`~Uy^|Ni?^Z7Tcx|FW0O0tlpm;s5{T z+df{s^E1rDZN;v4VP>0|8GkY`{`&t1s2YfV{r~st=KUYCl59Xr00M{!Xv@F<41fPH zoWA^R$>xvtF5yd$xc~X{7o-}f=ig7DY9RXc``_>1K-c{N2q2&ahQEIq{`~z1RCDs; zw*~7zIJ!sAKj8J}&!7K)enC|K{|nUc|Mwq|27mwp*#K1f8;Jh=2byv8+}GKw-@AGz z%-rMu^XK3HKtBLA{Qvdq*I%FsKn(x^1k&&qY(LP_Uw?q)|3jz0PF?=a(?4bU?qHzN z5I+E=z&88?2q2IKpz*){`~&I%+VJc5-=Dw#?LYB#!lHLRL7BZ8-NZ5x*TXaKmf6TZ20qEQj`^F;~x-W z05U+*fBxNj@_W(7kJ(ujGnWgh$g%(Z2hso#K#U9wI~hQ+`3Dk_7z`j1tL#*2FTcgW f@C+EF009O7>dRn2w6d?H00000NkvXXu0mjfueTzu literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/bb.png b/Mage.Client/src/main/resources/flags/bb.png new file mode 100644 index 0000000000000000000000000000000000000000..0df19c71d20d7fdc06e1cba01028983439b2bdae GIT binary patch literal 585 zcmV-P0=E5$P) zj51&U|NhG$_=oZTA7 z0mJ{l|Ns2{|Nr-Y#^3*c{=WU_!>_-;fvROCxPdkR1P~M0Xa=C_KR`47{sXG+pY!Yf zqu=)*{%)W0>;M0szkdJy_507SKYxDz`3KYh5I`&-cY{^``2$3Z|NsB`^ZWO@y}$qd z{r%_nFQBC$4Is{6hz5WF0@(mI8^j0N`~Tl>LzQ1Ye}a&q>MtPo*RQ`|OMu4x1!@2Y zAfSdne}TsT{`c$8-(P?J0c~)w`~?J`zx?#Dg*yp^z&88=2q2IKpt(TRKniTbum8-y z1bBWi0e#F0wgIH{FVJ|Pxj-8L0tgsNfByUdJMs7b-@h3^8h-!E3i%CU{QCW$1*GB6 ze~`2PfHeF82q2&aU>N+96yg32bmdP5238q{|Gydk0=52OVEN6!@<&OA7Z~7wL16>b z@DCt>7#SFn85mf=5eY;LZ#OUe_l@D-2Zn#Y82<4v{9|E|jkRQ8_`$&N0jL_N03g5s XX0T^_9W~6o00000NkvXXu0mjfXpthO literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/bd.png b/Mage.Client/src/main/resources/flags/bd.png new file mode 100644 index 0000000000000000000000000000000000000000..076a8bf87c0cedcce47099c6b74b59f2c9d1dbce GIT binary patch literal 504 zcmVCcfPV}OzZe+)fYC1)`hmpw%>WR9L@~Dk00x0r!@5ksR;+hV0>ZLfIcBS@orh;x zv}95za5W)x_7^}bV3YqpdH?h;NZnuSC%?V#{+0gl`~RQckJx{&l>NO~;@7XgzkdJz zEh{VwR0LSuiyWGR{aKQ z00-R5+27mwp+3@G@Um)kt zzdstU{yWl9SIB_0ft3II3)BD*KrA2|{``^Tm;4PDy`=a1tTre>fEI#8 u{{2z<2NVVaum*qtVq}m+iAaI~Aiw~?reC_kVQV=60000gsB+3J~Dn`pdxgmx19A{hgns>oXkhsL8>j&wfIu2Rsv+R_ zAB5T1GyoL?1Q1BWpFe+p|Nf1n;TJ-~Kd@?uhChFRHUI<=#0HS!U%!8$YJib28yLY( z0tg_G2B7i3e*FSj@aq@44gY_`jRywQ69xu=0D?FP=vyd&x*M*V6|CV8P{Uu4OBfgc z0tjNmA4y3`us8lPG6H?}8%zRafgb(Cp!5f*6oP{xAq} rF#LPR@IoIX4%E%Yz;K6w0U*EtuUcjA`-_J300000NkvXXu0mjf+>pV; literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/bf.png b/Mage.Client/src/main/resources/flags/bf.png new file mode 100644 index 0000000000000000000000000000000000000000..ab5ce8fe1237a18d6809a5570024eb108cb14a3e GIT binary patch literal 497 zcmV@|4`Xj5kLT%`al?B=W5I`(ov;U*021-Lr1*!W5(gW7O z@cTDV13&<=05Ky_HBiN$KMcQt819e>gHP`g_|Np;&&;0(C^$RHS>olZ*Q41X9v27}ar6#W9}0h{^{OajIK0?h(y z_zOe~zZd`lhy{p$|NA4!EeST}?;nUxkP49VfMx;1;V%&U|N9rH;SU2q05LM8GXV8K n(H~UyA0);fa6B_H00bBSQ>$p&WfB*UmB!B(-`wPhZ^#?@C^U5EbbPyl_g=3Hc01Sh${{P9=HB%4| z34*<3-m=C?^;L%miR{xQv2*hU;8+Y&0Dxf-W~KjsusWFAC4@j0#9_j;X5z6SjRhH> zd}sd(7FPhVFdSi!*Zj@;_Sc`kfByUdk|3A-`STmZ_yb~qxIp9o{E-p)bLt=iKmf7) z+|Ix(`{&<1pm88mK&}8g1WG~}e||Ik`OB#Cw~m1UAb?na(Zlrn4}+v6P!1@{@CT$5 z&-#|8y{IY2iKmf5omHz(^qAp)ym^JhN+jk7Vet{H$(Z8=>|AV=V zva&!M00M{!p&AHub^kXuF^G#pRQ&(-8^nMkpk@XJfB<6o_wOIv5C1P;X6Wwu|KUB< zc%X*g2-OTg#J~U$KumBafq;(A|A`a+OH08N{X($;=pP0KfB<5Fs|GR7oM!0i{{QYB zSm|$=!=N@Wf}I2qKuka<{f7YtBjf)QCj1u{`~UM7nrdKh0c`*VF+c!8{Q&j%sZ;-Z zd;dRshENS5{{S`og~Sg)05QIL^-54s5b7kL(8GuS8yfz9{>%Ui`+q-vfW*P*^_43C z0mK4S`s2qBh&}(NP5W0_$$%QQ@R)`GfB*vkck5Jby^kNv00000NkvXXu0mjfy0pt~ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/bi.png b/Mage.Client/src/main/resources/flags/bi.png new file mode 100644 index 0000000000000000000000000000000000000000..5cc2e30cfc47452d5bef949628e955a522d59e50 GIT binary patch literal 675 zcmV;U0$lxxP)$wbBBJ zMTk#;k4<3e#ePFE;{?TciOx=-NpIdg`t|#dcz^$YFqm+of7AU<7UGr+0t^5F#PsJM z(2)${Jd)SmT+KY5@!|h>po)L5-!r~>3uJux|0`&Jz~QGy6a*CiGyVtq1|Wc#zWw|0 z_y6C2|NnAvaR2)A>-NX%|3Ci{R?!hvH~jzU``ypCK7Rkq&&~h)FDNvCApj5nF#yj0 z0_X_*{{Q{_|NQXv@Av-qBMmR$teo!O*6-%u-J+8r3n~2s`s?uP{Qmp>{`~+900M{! zNC9;}eE(3IS9-C{62AZc<>Jx>%af}0@?r&05Jg0 z{{#T;0Os`NWh`ko4>SM%|Kw(60099Kad7|t0qcf^`tR-^sIP`Fhru7lkI#w#>Hq?W zkzqQ+@08y_|9v}t;{CEE%)%nTc=-MC@P)7S@!~g$((Ud|+28Q224FCZIGXF0FQ1zcbfB*dX_lJRr z;s3wCNa|2D02KoSAbE^!0Dxf-mZ69ApkCb5fP^9ydGO$cm6w`kGj(t#`M{tFlLo%j z*4%mm2&CaJSn02SzkrtfWBLcO;r%bLy5Gnoqrh)qC;VKmZT`F#yj00W%wG zw4zx17##T6*Z=hN0Tvey7!|wG?K;V)0{r{_`}_X<{Qmp<|NQ*_`}@YSvI2-@&cAgG z*R?p0?_>V(@&EVlEP?{x|1esA`||VNU68t8zyJRJ_3zg&hTp&0WMzR?0R#}s1|E|% zRV}tRkH6o2#3;(nsVMXI-pgBmh17ms`TzTu!pfBl%*_9R;OEbWDJei#0R#}sGpDd$ z96Spi@~bn7G5z{~^_!ZLy2#s;5B~o8&G`G*|6f0Ap0WS=_rFd2C(ubi4FCZEF#yj0 z1a!1=2e0}MwgDi3SU@2S4mePl{012eQ4J-3 z|Mm`933BS6zyJRJ1;ztF0D(08|NVzmQWB*4&)@%$um=Y`lmy1W-@hDx{{qQh3=9AP x#JH7#;TJ3-|G=X54_FW_j)5c_0|P*S0RT0dOSD~(4;cUe002ovPDHLkV1hfJDkcB` literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/bn.png b/Mage.Client/src/main/resources/flags/bn.png new file mode 100644 index 0000000000000000000000000000000000000000..8fb09849e9b5712e9cdd8a2c25035da201535cf5 GIT binary patch literal 639 zcmV-_0)YLAP)8n_8%|dG+hlCr-g% z|9<~wU<9iE{r}%DMphZ1Jpcj3%*DZBWo-@72DFWlk(-gj@!ngb-$#D&C_i9e{QvLY zw|{?r|NHlgiHY~?^IHG`05Jg0{{(k=dF}7->+9=cXlNG}79t%T{rC3_t(pA#`tSX# z4jKOh|NsB|;rr`({{a5__y7Wk5omBxQSrlv5B>nb??0VZ8qCZrpS+?}gatn>o%-+B zPl=+||6st##PspQ2Y>)#{Cu5(QJ~_^?faD-pMV;E|7MwLubjQ@B-gLso$mVYgECpU zxpfp2fEt*XnBKpC4-fz`0M7pb@zVeZ8g>5r-~k2r`Sbhd;{WmN02I(80QmHEv)Ik{ z>H7Nm`}_MdF)?FhWi>T50*LYBMFw`s0-y$le?J(QfB$Fr_2c8e-`_!j`0PG|ckZ*_ zzkY$-`sc5Tips@{7Xbo@1sGC5IYwrQfB%2{|MTZR!ygX*-%McG4cZmTY ZzyK0(F$K@T-Dv;-002ovPDHLkV1la=J3;^e literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/bo.png b/Mage.Client/src/main/resources/flags/bo.png new file mode 100644 index 0000000000000000000000000000000000000000..ce7ba522aa7e948d581478432643c230eed1a658 GIT binary patch literal 500 zcmV^3LvnC{|x`%yaFl$ss8ha0V@6XKS=c-5cwO(_{}IL0ki=i zfLOjo{AE-9^-mn=h(G`Tfz|!{{r?|W+uz?{^yeR#!Nm9NlRN`J0I_^YV&Ikj@%Q)F z|Ns9m{0FN3^Z!3k!*8HnFvf4N5xas50L!z?-wXsAPk7$ ze<1l+iQ((fPYeJ7#KQ200VpKFA^{3Nph^G!{9^b86oDW}(Ek1R7ZiL9e}Ret0*H}e qBRB&8AR!3%7c6cef(%d+Aiw~vBYd|xMihYn0000j-HAXl7XJGjM{~r+i{r~sxzrQwG-&h#_Y&p;L z=ieV7_s^f-zyJOD{rC5(+EoAn1k&)I;s3Kw&;LT?{{2}P{Pq8T&j0^^J4?LvUd;UK z&+k8ffB*XXONL(tXahh1fi?X94^$0Q>Z$uRRO)Y4)uapmB!2(-ul(c1=C+V!kAF`) z$PCo;`_FHns{jIs3Fu;wy-Z(c2YwAz{&V4aXk?bejFO*Op&u`@i~PecqBpL@DIoojb8mb+WHGK%fG2>)a3=iVf_~v+`s?*1;!`?Kmai^I3i*ZIYtpN f{g(k500bBS@Ns{bTs^2gCr95H^s6Fo1}O2_S$Nfl}4;<}o-r{^R8Q_xtzX z|Nnu?|Ni^;2gm@D3=F@4EC#P1& zzuV{ie)#0qf2Lnh)j(~3VeG$ufPMl9Aczeh3x56n#l!R`HRQLfpFjVwfkN;%I39k( zVgne^e?jDThF>oL0*Hk{T6(^^8pGqq44*zhqZ1OZzkZ>_F(mFeH~<0+0A4S6=>Lb* QN&o-=07*qoM6N<$f&=*Yr2qf` literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/bt.png b/Mage.Client/src/main/resources/flags/bt.png new file mode 100644 index 0000000000000000000000000000000000000000..1d512dfff42db1ea3e7c59fa7dd69319e789ee12 GIT binary patch literal 631 zcmV--0*L*IP)?&DC3JV7l?ccjcgB=Qq4l4w|Q;eOZ4z|IjsEI`#PYSloM|AFHF zfy)2>XZQ=$@t;lU|F1tRzpnmg{PUOLH{-8A|F{4CvUU?d0I>k|0TuuM^_1cNUy$nm zzZn?*{^!*ACzSEy$NyhHzB0Wn`TXRU=!%=n%Ci4h82|!^32gR%glZtk^6USnb00o@ z{`LDW>%U)&BK_|-y-75RkKVi&O=|xC<=6keK+72b0*D2q;Xlai|Ns9mF#h@fM(h;aV>^!x1-zNu02XV-n@ zm3imZ@&EI0hChG)|NZlafdL?Zn1I0yH26P<=DUx29QQsc%}izF;QV-S-hYOl|1V4z zX|a-<=Kf#g*Z)7i{{y|szyJ_HEKEEM|C#>$KmJzoZa)M2-~Wm-e7Cl+vi)L|kY`}} z&A$F20RV*|BkB*O Rz6SsR002ovPDHLkV1m;fPLlut literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/bv.png b/Mage.Client/src/main/resources/flags/bv.png new file mode 100644 index 0000000000000000000000000000000000000000..160b6b5b79db15e623fa55e5774e5d160b933180 GIT binary patch literal 512 zcmV+b0{{JqP)O=a{vExMP2%`MCSoB^FIcLe_%lf;|~%E5I`(IQNh}3Ao>6Q|DFUXMn*>AqQd`w z|1kXd^B;tM|Njjl{{hM0zwd6?1Q0+hV1xeud-4=Wy?p-%sO`^#2S61Jzk!N?s)6X& zzhA%p|N6}=D+{y%Ab`Lc{sL9~1=0UN4*CD*7s%9KAf+JHKs~=eB-8KTKvw|-5R1&; zzd&a|ob(5%^Z$Q=wHy9p13+aOpFRNu5F>N&`Tk_-7w>=n{RejQzkfh&Kn{rf10?_b z{tFTZibx5v&dxav5I~H7|Ney-|DWN1$%1FyagzUW0464;_wU~W1Q5$TW@eGxtUvee z3vAf*8|igK9~@*rr66bh|NrkNM8z+V zAV?>O@ek;bKfu6d00k6 zPW&$KB`I@TtA?2x@Q~7pdcWi#1>DDZ2>MuG0I@IumHuaV^&7|soAMvT0IK){RtF@1 zgH;3B;_Qq-34j1%{P^)BFE1~|NkA(gBv!!4$aw$$eSiRB1ga=2D|__l(H|iA4T2Ex z>lc`SQ9x2&UjE?0g8%`<0`lvhzd$}*14J4{IhY2@0~G@V5J82%q9;EBlXu0U&@_82I`BfBC|Yn~P8llm?myQ3_K18=`^X_ivyEfB<6p_xd%%#*GZ! z-C&_V5IGPDK|g;X*gypg3;+Sd!XPTj0Q7fK5>&$Y{sHB| zjK9DB00sa3QR4q|tc?L6fSCSm+3>HW`NxbYpP7GsX8sK%KS9yYPfWi)GJ)AFzdp14 z{>t>1fsFwmfLQ*Ci!yxq#8guv=_mQ;-(Rp{AoSPbXFRCwBA z{KboB21 z`ppP*BG3|`!vO*asNvt=e_;3gWsp&PA@E<2jYs6ilRy6-zmSku5?~OOP=5aV@4sI_ zSN#QP00zQ!JD>SpAKYgR_QStF|9~F;2M|Ck zz%U1e+FyoWe;B_0VVclq;}vp_kNy3hKMWrj7*{kqJbMcaQ;_Pve;I)x1Q0+h46j}> zeiN4E1lkA+>|e|*mzJsW^M3)l^W_^hf&c&c`Mv->`48yHFF-%s0|+3-*GLfwiRNz% m3}3(u4lrUsie?}H2rvLBbQv(L;??y40000e`b1WGV} ze#gbd^&bp=ef-GykAYc$@BhDltSqdeSy?RXJpcawy+5fJAb?mHcsPJ&{d@EF@4HtJ z)qno||1xd*zaM|Ot4f#|8A1AQ-~Pubz$7IBv;-i4SQvovfB*ge{fFVtAE4aNE7yPi z`^Eg=5yP(^Ur(Q5`TLjM%#`8#_rHuB|9}1hItd_vSpNO`#qjs<|KGp=|NIHm@MXqK z79K7}ZvOwQ9Df%s{`~lX*xI%KzW-!k{Lc96Cr|@G0I@Lq`0?-epMSr9|NHeD?&umBG}6iH(DSogJte zDERNk_kV0Zng09%Itd_vSb&=T{rt@+B*66dJ0my{fpNkvBm!jpV_;xnVgd@XDk?Dx zh=IJyzyJ_HjJJUrz)|>#fr0nRl^;KTz#|yK`2G9$BS%LzureS32rvK^u`=B;c)+&+ O0000d94tALrX!Wj0S6V-v9sr07*qoM6N<$f?*uARsaA1 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/cc.png b/Mage.Client/src/main/resources/flags/cc.png new file mode 100644 index 0000000000000000000000000000000000000000..aed3d3b4e4467c33717ab3e2f61596e06113f9bb GIT binary patch literal 625 zcmV-%0*?KOP)Vbu`-)NXk-Y`p?2$B#e1 z{(buQ`}e;;zyJNd_UF(2w_nb@VUiUP2igD-Kuin_BL6vl`zgIQV*mR8|8G|2U!T7I zn`-)Ow$bxne}9LGfB5z5*XcJ*K(;aeSD>o^0st`p&i@1e@&c~g4N@7~{}B9V!5sbp z`j|K8<@Nq-&kt=P>@pAZ`T7Q#;RiPl^j;kSgdzX}05Jg0{{#R8=f>y-z~LG0`u_d< z{QLg=2?6_k)DH^)`=sFo8YTUi-v;{r`o8J{6Wj>^)c^vB2^cJY|Nnmc_5b@He}DY@ z_51JdufKoSslWR1>-W9)3`r;XfsXn0`_Fc($H!lT!V@5Xn1Igu{qOIuf50&L{qg7D z?|*(LofMGa_%_k-*{}b_0g?9mMSwvE)Bq4bEIr*1})*zF_+U)&LMdEMOb{Nb-vOh6FrNC(tkd{&4;N%g_D$&)>g> z0^hO}m?z%iIsOJ1mka;_#K>R{i_RZNQ42x8VJSe8f#Ern2@qfa%&RHCyvg>Q00000 LNkvXXu0mjf=TkSf literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/cd.png b/Mage.Client/src/main/resources/flags/cd.png new file mode 100644 index 0000000000000000000000000000000000000000..5e489424884d2ec9e429f70d69af00edf242a077 GIT binary patch literal 528 zcmV+r0`L8aP)JkPqeYl28iLgD=0{><0$P44T5yOrT$dE?(KkwMFdoG^-J zGv9P)Kk|i5`lcNgUUAbboca5{hI)v&h!9!~`Yg)Ld}$VwYqqXn@gVLi>3LSVGm1W? z3qnDJAk6chH(u7f~FohUBCxfQDx8?5BQsCcprAnfVhO SHC~zk0000@|NZ~|iEi8i zh~?j3h8^dV0y&$fY|i?}`2XK;hJSzm|G$6#|L@;V)&KqZ3sU;$?>`_ZF38C6g#jRd z7@s_ODJ3Nev=9gw8UKSQhX4P)z5oCE{eQ>y|1j|H-+x9%#`EXT0R#{W1H*5i5@u$g zs{agN2m-%;fixfipeaCQpFe*F2q2cU!)f`}`5%6M06O*WzrTN({{8*;CkCkD_wPBk z=llWket~@P=ieVecEOTuB>(}$vTge|RYlbYUmpDW_v_E!KfnL|{{8n?G|>28zowm@ z_UrGj-+zAtrGTUoztWctUjPD#h2amw@BhDn5OHz(52cFPg zot=T34qlT?57Mzkg5?0ssOG0Ftp>paW8OyZ`_I07*qoM6N<$f+2k} AjsO4v literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/cg.png b/Mage.Client/src/main/resources/flags/cg.png new file mode 100644 index 0000000000000000000000000000000000000000..a859792ef32a02b41503b5ab5f216191af397e02 GIT binary patch literal 521 zcmV+k0`~ohP)i@P`2b{=v9^Ktp0uZ001fLMSM)r!?%mH+B91 z=ii?+(ckX8zrd=&8g~5t-|*}I50LQ;KzRlRfB=Hn08;wv->)Q*Kc0LLgTdsU-~VfV z{r~#=|1XdRMzE6r0tlo5X#B6gzhXrG`18Y*0!g6iieLY~Kvgqz}`W zLWO>Z3Lv@r7+CdZpiy8Ae}EeP0z(_<2Y>)#Vfe-1#`njMUlORAfdMG=o8iiD#%aG9 z#Q!h|feZ$rzkeYJ=p=vuV)VVru?7U|?FE@Zm|i>fBlzF zc~M_qKf%h=bAcoS;}NE7f8|yFe%B9?;;8%o@BeG_!|(4qhyo=(h-XBmKHXpc{~y!A z`THH3fsDVeudko)ARzm9UL&JI!+~uEM*rBES1=kd6zV%LH0J*N$gIQAc0y}k9qTFv z4h1oVG?rB#zNY^8{QUp5wE>>R#S4NZQd1i@F)*?OF@6y}@zmk^!Gr7L9asuAf!ae1 z{{CbBBH^Az=(+fAb?mHzA!MjRs*H~|NF=I z{~td;@BjZG-hUVsqZ#w(|L@=b|NQ>{d(*K^00G3pzycQk`jp|{UxvSb;p*Vv_V?Sr z{{Q;@@3$6WKrBG#F*5vR z`2XiW&+Xo6{KsbP_-SF+DJF`pL$*;0gaz z7NKVhyo`U;4*0+SK#>rfFFfafF5DUYXKh;r+@n*scPQUy6`!63S zGXn!71H*qtMn-04W+o=4|487=moEST#KiEAA;m(3;Xgy1iO{3BKc9a12~-UM7^Z`) z1qdLZI~bN-c=z}J|D88K{QmR*&tHcBxB*ZDKmf5Ya0q<-!`is-{h$8~f7m(x{QZSX zH3Jv`1P}|uy*o^wIGI2G`1kie)4zYe_&EQ6|NftW0ohon1}3I|Z{7d|5aVBMp0ssVo0Ek~;W>Z#0SJsA+2j`G% zv|UVeYYs-#Sn6_J90h1VosR?LBU7{U1rQ6+R0f9sPrg3=`~UA>#=n0a|7Q67_y6zT zP{j57|G!`V{{zu)Mn+knB>(|98e;E17s>tTLaMG-~Y>h{r`!g0jL-tfS4HmF#P%V_xIo5zyJLP zk|2W%e*G8w^1X|KC618W{eAoCFX+EI literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/cn.png b/Mage.Client/src/main/resources/flags/cn.png new file mode 100644 index 0000000000000000000000000000000000000000..89144146219e6fbec7eaa89e1bf4b073d299569e GIT binary patch literal 472 zcmV;}0Vn>6P)$bmtfBa_T{rmsVufH!rO2O)W0!b+P{TrwO zAb?mv&i(NBbu;G`sX*)cv$d*%>MiL-(QdhpkjakVmyu%k;sfcNRj;yhJaxT5MTg0u5&QfH8#Bf O0000XbC_7v4G9~kE;3? z$h?1GrT>4y7{3|*{r>Zd0U&@_82$jY{AFPH^Plk#hy=QU5o|Ds0oDLyFn~2M0QE2c z1P~*G;PKK9g@-SH{rUU<_aC4k|G@~v`1Kn|{`&=C|M{yZ!G37cNq_)iVfexD=MNA8 zh5p0Hf4_c1v;p<}|Ak}_F!%rh2&CcXZ-(!`82&@8$XA^gF6?xq}zd#y5N`WeVBbyB+frxZH0- literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/cs.png b/Mage.Client/src/main/resources/flags/cs.png new file mode 100644 index 0000000000000000000000000000000000000000..8254790ca72f98d9e79d94bdfcb8839b1fd434ad GIT binary patch literal 439 zcmV;o0Z9IdP)|s3jQ%L{DG0k=ogG+0SLgMnA-pV!aytqt1(q3VU5g2cTadE%W~lnu`}m; z-504zOD10s2K8+~RqF^K7O;8$o;>*sR0>r0`!|sM`x~eNg#P~i_4n8BzrTI~MgGdl z@-i@d0|+1%Q&SyHO(9WHu7Cgk{{@--A8b6x2B1zb1BygNIks=N00WJ{7y^0)%zz1kWJN^&bany+5X-;o*O`wU`+x5q12BMq=+`e0f-vAB ze||Ik`OBc90(3J#0I>iAmFf2%21!Yv97q%>_xm>^i2a8FLV}HlAfRG^0AlbRVNjfdgKH->)<41fMG{Q1Z53yA)}NHF>VM86o=*Z=~EMTLRklg;&|8vY-+ zh5rBh|L@=bKM?ZoKOp1JpMQV<{rmmr|L=eQfFkpA_5cJB<7Wm2?&_*f&z|!oCH%Iq zVPN&@1^xa7gnxhi`TP6#zh6KNfByZH;9_972M|CY z4S#m*VEXjw_teQhfB)LctFzlGTKpda#8eRa1F`|=V+j!s1_l{`0AhU0z`&K2_5c3; z?|=UACdB;@3Hy%?Ffai9!1(6P8-M@;JL%7#4}bn}Wu*W1^8Nqk|9@zxfJk8Yg2Rge z5=!5G`~e6c79IwMkAMHSMfCl8%kXF6O^EHl82Alg`~}6s-@pI={`>#;Kf}J}IRF8~ zc$$I1RCFdZQi0L={}0epF!T#d{sWT?V3G+SzyRi{Q|!NXWpDrh002ovPDHLkV1m+z B7Bv6> literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/cv.png b/Mage.Client/src/main/resources/flags/cv.png new file mode 100644 index 0000000000000000000000000000000000000000..a63f7eaf63c028615b2ded5878b5e14a7dbe962f GIT binary patch literal 529 zcmV+s0`C2ZP)*82p^=00=`tlmIeOf&y&dk@6oT z&CV8YCOfMzZ7b-;WHc4ffO0*K`UGc#9pHKW{n=1`s=Y4`sB0UGuD|F2(+4xFrs{6A+r|Fh*S$P>SRGs(%l z+O-QHfEa)O{tdAK=ovi517(5A-n@AO5I`)f0sl(Vm><0RZzq2;(QtLuuFT6X6;y@p zb-8u)9gSFj-3}5^z@SqUX4qfA01!aT3<}j|#!Osn3@^V(zxysO&hx#~ZN`f)vS*$d zpLlA?$HBnM&cM&k$j1o^^oKjY0t65vgZ)>ehy*ei5K#*ZyWju7F%Ll?01#jRixNU5 T4U6zw00000NkvXXu0mjf>cZT~ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/cx.png b/Mage.Client/src/main/resources/flags/cx.png new file mode 100644 index 0000000000000000000000000000000000000000..48e31adbf4cc0074f40e95f87c1f103b91fe270e GIT binary patch literal 608 zcmV-m0-ybfP)Uz~~Q*L}q{yKmai^Fs!d`Rbj9@@=xl~KZbvQ|NZ^*|L@=bfBpc` zKOp-38^rz{DEepVv)`L`Yyt=%76t}3hHqR?<~aN{I`!9n&u`}MKs~?z|Ni|Cihlk3 z1w?Q7fI4M?HUI<=3&<7!82Y{{Q;@?^mDN?|=V) z{r~^ll=rtu5CcE}F#**BRkyY(adR>Mc_H{~T5^XRV~oVFSgBv#s=t2z{grn0SJIVV z|Ni~D^5u8)5(a<(0%_pmV<{@S{pr)+Nt0E6|KyV7{&xHKulY}Z0S*2Ic2cIyuhSoY z&3*a@=p=vu0%~ALNs$10lZEA9e*W!WzkWV{%9A7hYlh}ewm<)V{rd@2{rBIm&Idr} z{RV0P2mmnv&i?}d08{q%2kh+oUtbt0DGA2L@2{yn^Xv%Q%_4hjZ2Q{KfBuh*0fWjOcoPYi>{Qk`ejtlm`TPy!Q@c0|K1n4Ay00P>u ul!4(JQe+}W>@P40kp+Sq42=5$0t^6?P(4CrvcmZQ0000s1`2Y9I{~ve$KWO@Yukru?KTxgz{s9CK3*7Y} zZC_6Qf4TJkuQ&gHzW)Di+kXz$|9k=eg(Cm|XMz~|=g%L20Ahjo4`KiVr|y5|U;qEV z`p@?NKb!P_9;^R?@o<+hfSm*oKp+h;f&YIQ*bg)L>I;A4WVP82*P-nR{;Wu@%{Vvyu7@~4txIf`R>OD+-01E zIfRil07L2S-Mat*#Q65@TRuKMWMi;}EJy%|ff@h;2;_%%@7_UT@edf{0+7H22rvMY Ws9gjvbyTka0000? z0048MLcfb{@Lpld*gfdL?Z zSQvhRtN^J#x6%GQNHxSfxHgc;AD{-1tAIKH0tl!9q}uD*5!1Kl8Kk6va!f$;fJ%WL z`2Cv^NdEc54D$xi27mx!VfgXqT8ME7!~2)OPy-`SXu#NiAkhzFFflLy1Q-A_8F>@M S6G{sJ0000h<6BFn%a z@b8~2SoNP@zd$;E{sbbRuHQd?{QCI=sNwhbA3*&Qe}GP900=;09NYi^fU@pUdVa9*13;+Sd!tjgXKhXQEMobL97(p6<{RLvMGBN!7 j!N9=G@a-1^K!5=NcXWu!7_DDe00000NkvXXu0mjfeQx^H literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/dj.png b/Mage.Client/src/main/resources/flags/dj.png new file mode 100644 index 0000000000000000000000000000000000000000..582af364f8a9cb680628beae33cc9a2dbe0559f4 GIT binary patch literal 572 zcmV-C0>k}@P);we;9uK`S<6~zaM{qBL9B<0yBR7V`E_e2q4D) z|Nnpa!Ep64!{fLA8NdLj;oraifB*mg`;Xx-kn#6Fhzn-q&in!pKrBENJRA&WD*ySo z7*5@0`26EP69WTC_22)0z>5C-g{l_hVFa245I`UeKudt6h7^Mc@BgDW81BCO4-x~L z`sXhc{R3+I%fRsKA3y*x{R6rHsA1>M|6jifb2E4w{z{xB{`~z5CPC;Y7bk|9<`c2PXgjR$^CT@Hzz$KrBEfF#?VC z^aSdB^z+g5SJMxCJOGqNsQwQkfr0#&=?~CJ009Kjz|71H^!MIRd#Ajb^76;aUyQ$y z%m(TN#spBq-#`C>zGeUjAdrR+|30kwu=eoBL!3-pGMq9%bs!`E|ACMovw

4;Zk2 z8GbPU1Q5%#7t@Nb6*GKbU;u{yA29j{CVzn$|6qa)V3LCYAiw~8(_SNKujRx50000< KMNUMnLSTY(1rd4x literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/dk.png b/Mage.Client/src/main/resources/flags/dk.png new file mode 100644 index 0000000000000000000000000000000000000000..e2993d3c59ae78855f777c158a6aae6c1fb5c843 GIT binary patch literal 495 zcmVh!ZNvLM`<}kPiIA3?K?Zl!VJuS0ABN12uI2v;s z000mK68GQM4oDR3?|C6;zBc4LR82Q1eETXSa+3nD0Ad8%4|Ml`Fn}2U{~ypshW{9V zk%{T!hYtV&#KHiVV*o?2zW>+&Bgm+K00G4EikX==E9>w5`yf~S`o*<00G4K6dZ++hy)_Bw{QPEdi2K7 l5H1Kw2asrHVqgFWFaQRwS@oh;XP^K8002ovPDHLkV1foV*8Tth literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/dm.png b/Mage.Client/src/main/resources/flags/dm.png new file mode 100644 index 0000000000000000000000000000000000000000..5fbffcba3cb0f20016c9717614127b89db4c9664 GIT binary patch literal 620 zcmV-y0+aoTP)yt}{r~U(pZ|aU z{rk@#!dhJXj+vP`D&$&!2yPR?Jud5I`&tqo00z_V3?cpb$_)%#~li z5Bw?p&G@T5&5h~tB<;UIJ-`3_mgbQL+5iwhOdtm^{QZlh+Tg>NcGo{qR#UR9ewx4g zlzHy^uRp(j{rmOj?;oHBfB*n70M7pb{ow!s5r+W$=Kufw0RQ~_o$a1N^GPoTT-yiw z&XGVBSbe$r0&xrf|M~#~9P-zx0st`p&i?@b004Y@cH`sX`~3X;`}>j`1PlZ1WGkKd zF1~FN+w&9T}cJeJULy4V8r?0tNHM6WN&<2126ppbC05A-~q$vMCOd&N)3^rn&3WaiZo>@dB zxpL5=L>h@#UjVT%{Q39iA58Ths0J3s|NohoLF&Lt!8(8c18V>XAjZFc|1vT%{s#lF z^Kb%2CZ>-cJ^%y|<6Q;@;r#qR4;}z*|Nr|B$h_ab1b6QI%fu2>dIV_O?>~RRu82tM4=f|&Ke}8;oX5+eBw}y?G6)5=s|Nnnr@aNATAPEpaEN{Mj=m^OD z&%p5S|G&Tg{{H#<7bL;LEGj9<&cFmz{_j5mJbLs9Ab?m{m{|TZ{D1lB9Z2clKfnI{ z`ThHs^2c9)q;CF`l>Eoa3N#g>nv07INCE^93jWH3lKe}28-VENu!{qxMpm&svFOiawo%#4hT5U&FS z5Yzu(KY%Xz`RDh~-yl6N|NT~b@qz9A2lm1$Hf43G`D-AmnZXVNY5)izCWas1fbRMA z8?52ykLBVV-&q+tkKFpbbOC=r`2SzOfQo=l0_p*hfB*gk2q2IKpzD7Eo%H+Hk6%A8 za57|Q3rv;f;d1r)FDv_xg9F*eKs^8f#KQ37)2~0jMR){${rwHH2k3S7pO3`Z{#jf7 z{|`16Y&=9YkOT-IMh0&|hF9+yelRe6V}O#tcxPkSx96}BCoe=1&?OKCkOT-Y05IoG U$(*n^qyPW_07*qoM6N<$f?|9Y@c;k- literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ec.png b/Mage.Client/src/main/resources/flags/ec.png new file mode 100644 index 0000000000000000000000000000000000000000..0caa0b1e785295d003869330fc4e073dce07e7f6 GIT binary patch literal 500 zcmV1sCzZm}g`N!}J$oTi?|G$4gL7*^D3`7C}Kmf5Y{CmeN)&f@km*M|^ zrvE_l-~a!AA&BAspa1{=fXIJ9!9O2vbOQts3j+fX{b%^|8m0my0Yd-4N`WN9@BjaR z=no^SIM8~400P%s(0a|A0FHhM?*o#se9Q zEZ;b|7ytr@MWm#zEz$bb`!9d~{{Q>$@1MW_!07MqKOpw+zkh)g(B$8L|49h*Ov!x= z5I`*NZ%IAX?Fbu$UUAQCA3`wxix_2=I& zAouq_SzðCcuS!~%52Kai0?gF&VORsRAR2~rJG2PFT1^!)w@)C_d-AAkU2Vc75Z z*R<@y`9z1vevIh)-p7{p`5C+7f|6l;f1_&?! X)GmJPc-xs)00000NkvXXu0mjfGFPrC literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/eg.png b/Mage.Client/src/main/resources/flags/eg.png new file mode 100644 index 0000000000000000000000000000000000000000..8a3f7a10b5757b006948ea4436fb242d02dc9a4e GIT binary patch literal 465 zcmV;?0WSWDP)LAHVtk{r`=k{y)(2e*gi*sIRYISXlV_^=qKp{(!;n-+xj9 zUjemETFMXP0$m6sfwJP_;%#kh009JYeOg-Dy?gh5gTXH_fG|KLm<2Qhs6|CZ<>JMQ z009IFR-loRl9E6vpeV=!FaTTi8)D~Q7yv2;2q2OXK!5=N{?|@pNV(X=00000NkvXX Hu0mjfG@sA` literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/eh.png b/Mage.Client/src/main/resources/flags/eh.png new file mode 100644 index 0000000000000000000000000000000000000000..90a1195b47a6f12c70d06cb0bd0e4ea88d7bfb03 GIT binary patch literal 508 zcmV`hKmn*~pz%QT=MPXRKmaj;?1ifa0xhloNlE|L zuK&--1mwx?-uoV+`qwW8u#*4+i1F_GyFwg7fByafIr{%Uh)$p>Ae%w(|Nq~=m~|`# z{`_H7QhIal96$iEC^sml1*F~kc<(nb4FCQ91q_kDz!3TkBLDsd`Sky<|4qNomi`s~ z`xoeVfB<3v8uI)9A4wibp!A=AfB%5B0nwj-e?TNKsQ>@@`|oeijK3Q@{{o!^5I~F! y*$fQd7#Mzm(H|HCnf(WhfND7yc3x%x2rvM-AWsdQI)rrq0000&KpNnxA)<^73~VwoKqoOWF#%15i9uxn0tlqx)vH&?arx`ryF**H{9<5mxO9m@ mNC@PFfB*h~9RdUZ0R{j9;Y1$IN+(bN0000&(jx%j7OGE_~DVuFcQkgj@33fJv($pjj zgoNxWFM>pG#K4X+%S_Ys!f>f$mib36%ekHNec$;y5njB{!+Y`YzVGwA&4mTVh%ikU z03gD2Hn&LPeNu%hDG7PUvzrphs|^(-as$IIo1LmPya%Hc0Qn*6qc4XX3oKoa+Z)_XBQk8 znPA)XelBh#6J<)fj|w>7X+~Yun^@Bp4$+N z6L8rb{%QnJN{fql*fJH1L*2YjUlB~CXS&&LY)1V3h&68|x1_5-(4l3HUgs~3JvLXI z$_D=zL{dTnq9RK`-w~w|sCYqqA;@OoAE0!{9Gi+cF%zA>5*8OAiXWs z!A~!@Tb_6WJ;mn(q~>CYJ~Oq(|Mc`miY)G1d$)?S_lf*=dz3nd-8+hwz5w#U=!7L- z+Ve0W8Werm#o=KvYxRVVNtM9!poHk%m;Y{gxKdXC|Y{ fc0^aUlspXz7vm>S7OoCUZUIwXLGJ6E%Z+~lY(hhH literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/es.png b/Mage.Client/src/main/resources/flags/es.png new file mode 100644 index 0000000000000000000000000000000000000000..c2de2d7111e3cb59cf6511dd2ab045e824bdb43e GIT binary patch literal 469 zcmV;`0V@89P)@|4`Xj5kLT%`al?B=W5I`&pe;NKW0^Ri&h`xRJ_x;0v zUa?=y?0^3M|NZ~}FE9c#{{3cP{Qd6}13&;Vf!z&M{pZWqKYu4MFm$tgedF}w=P#IQ z7-9gT-$11R0mKA$(qEu4%ok$*y!^wMRm*x;`R7|k6yu?K{s8?55I{^|9{?Tjhec2I zv+6&FhFWG_BbNVc|Ns94tNRJp!0`V!Py;{!F#$2enB#XZaohg-5%Tlk#oa&nzQW9g zl0Y{D4gK?n0U&@Fe=;yIr=|V7caH%YEYL84k`Tt9-wc2LGODP&y?7BIfLMT@X8Qey zK~fSFpuiXa$^kk7RCwBA zWQbH``0|MX0{;DB`1Ob3-!Fz=zZw28fY1*HhF@R=VQ?@21P}|ur+3w` z_wT=dfByab`{x&s{PXYspTB>919^Y{{Qd`I{N9v10U&@_7=ExZ{APUe{`KE~Al1MB z{rb!Jhml3<_uqeCzux)%bv7|NkFAvw@aK@N+YWG5`bsF#yj00sZ{|0ReUZ0OJ4u`~d&_lgxzd z_7grGtKje;)ax=32IqJ>VE_O6|Nr{|0Uz@6!2*a0?AgCSJ_s@V{`!jztXlEUzkg2h zOW%AK0ILQg2A~)NKmdU>0L=y=29PKt(~s@sMWK)O87MU|4G0ImPt*+y7s`{{I1L_{;G9FVHyv z0R+;(^!pEkq$JpwzYKqVGyVRp{reTr#sB4{{{Q&{G@Ah!GGGK$3=lw!Ux87Egcwk{ eXi`7`5MTfy3O%OUuKb?>0000W@Y84;!#FdBh{DWNV{85G^eR=U#)*62qRvoaLX5p!7Lm^{g>IM$Q zMs{Q_?l?+2NRuC{19iOmU$>t9;*>tnC_qaIU+T2fR7nFyd0Z0-b-MgLN zL~_)yI%8}kwU1=sY!YIJllUc_pyhtI4TcZ)*Lyi~^>58dcXu!D%H5T!AO`R!zyPR# VdiZ30KxO~{002ovPDHLkV1g_p%GUq@ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/fi.png b/Mage.Client/src/main/resources/flags/fi.png new file mode 100644 index 0000000000000000000000000000000000000000..14ec091b802cf24ebd9f8825f81cd2f6e360b46d GIT binary patch literal 489 zcmVFP2AE)Ir2{}>qlLBSs|`Qg(SfB<4)VqlOE;Q=cD|Nnn$ zna02W|Nj1E&`=Tpav_4q;M$#E00G4E4{SI@`q`VGKvVzz{r4XmU}R+c_2(Zz0I~c7 zs`v*r?Dty(;z&PFFXX zRA5t=4x{1SIibD)Vqy6A2V^D4P_SySA|L?j2ip1XFA)9%V~_%1r~w2J3=}{2Oiu1 f7(RXZ0uW#T>&I!FfdIJb00000NkvXXu0mjfj-u42 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/fj.png b/Mage.Client/src/main/resources/flags/fj.png new file mode 100644 index 0000000000000000000000000000000000000000..cee998892eb316c3293ef2d52afec9218bdbbc03 GIT binary patch literal 610 zcmV-o0-gPdP)2C05Lr?a%1G+Sb3M_ z-f!*)-&mJ@lxC7weD@!u;s2li|9<}wjr{Zf&o8mqKR`Cn4*&rGF#yj01QaSLwCD}R z0w(ww8v*|PzTN}jB`Pj8{QK|!{{8;|gOCLd|L9jy6{oELG6Dcq@B)aDq496GGsCmb z5T7wXTnzN$?|=Wl{r{i6vr6{G)xV#={AXc)t!L+QBoiQjSb+Zc`=1dU2n>I~p8E|B z6OfY_{`1$ji1Pn`9_T5yZrhJfj0}g~00a;V(9A!7nZZWFG{8az7^c++|9dI@cmDl* z!Nvb)UorrL86bdIfbsI1fk}{;;V;BV|AE?oY(}v2K-{x07*6Kx`SfB<4-V2A|A=r2_C101z~ iU~vp#0R6xN5MThlzdwv9U#bcK0000}CO1*!he@c;k+ zfB*mg{rg{#hxPwIh8G{d0|o#7{Raep|AEAFCm#U_AQp((@4x;AD*Xo({rB(3@4q1y z(m>63@(hzyJPYWMEi%>@iRTXyxzU zAo>qT2S^W413&<={Q1WKlmMyz`(Hzv@8AD_R~~+N^7b200Z@m&0zc4{Q@7rNwftxJ z^$Q??fEu9g1Db8CAq;fMkDq@pJa`8*&sI~^TtyHla^%`8uswf)HUI<=3()%@Pl0^! zf8Ui?K|l%XeRX{Qd(n@juAHz(D*15I`UefB!>$cK-hRUqIf!|Kj}Y zKt+#Ue>r^ZHOMRf{y`i940V72V*2%m0pg)Q5O4ka4>U8*PAAEH2(%ZZ;ol#C0AiF(UW*)& q3=s4Oj6m#vP&UxAe?ZIt5MTgFMVEBke8_SD0000BE0lK=nzFYgc)d0A2*B+AFf z2joHok-@WP&j1351!6Wt`q9fjf1W;g`1ALFY=DuG5oiNI0I|ST{|2JJ|Ni~?`|A$_ zRt*pr0t5gt0M7pe4IJopi4@}M{rvp?{Qds``}+I+|3-D|`uqR;{Qmm<|NHy?`uqO- z{Qn;q1i_Qs0*LV@1A}N|@t-FT{{IC^{`vn0sPGp^)o&2vABgb_!eEtCyu9%!Kmf6* zGhfda5_|CT&#%8A#S0%rhKer*8VNG{57cZ3sU*g7is3Rq0I|G(Bf-nd3vr@r@vHy8 ze*OIQ@9-oMOb-A(eJ@7=Ab?mP;SCW2x*O<#U%#>Y7zqCS`2!F@APw*!ml9!S{vjhP z$_zA&0R;fLP(1(v#Q5^%OL#2G%0Af7VC%@R_vTF*lgG%);U`26kn-@hOg zU%!6+4+cOs(0HIde9xZz`}Onxub&LUB0x(30+2WcIRJn#2ut|?gWYu1Cf+!-K%B8# zdf?1WA}#uZ8oj7u>$I1i0Al&`=O0k%-@icgAIJnM0xA6maSq6BK-ECw|NZ*S`0Lj% z1_pot6puj;05Ax`F!=umqj7^frO?t|3^&I1kxUq9yECc+jQpY84SWH_0#pxl$?v~F z@*hy-KN0|X07U)z`4{NpU%#2aHUI<=%a31wK(7Du52Oc(|3O^?R1IN+RRjI-n*kVB z3=9AP#PZ|EACPLGJ%9cJNh|>9B%spYzZw7h1%?tp0I_@ndg9MNE>313@6R75NcceF zkr51-#U+7;F#`Sf7i0rK0I_`g_NQ&Zk)EZ(2O=d>QH$KN3zEi7S9u{+2K>GX4ds`2QcM z=+A$K-~a!^(JwH9Fn%*K{{Cdb01!YdV9)*qi~a#?`wdg{8%Z^Y!NB>w;@|&31~6!UgVU(k2*|8J(R-+sudaynhucHbwAMTnor{mwqO^w7JHzaBsT z{O^B8RYf5+LvDs&KmRKVd78=o{`1#HTiEo_OolaGleS)G+IQ#sUI`b*pv<`1zCJ=H0jd{{2S>p`ri%{LsXJ%FbMS z$#S`6f|?OG!^Jxczkf6Q`UNF{l0Sd`ad7zm>({^EzyAS6{{CgrkluOb3l1A>ZU2~A zK+FZ=zkmP!`TOVhpFbzBzFaPmD2$N3;+$pK?>zdet`f0002ovPDHLkV1gy;I?Vt8 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/gd.png b/Mage.Client/src/main/resources/flags/gd.png new file mode 100644 index 0000000000000000000000000000000000000000..9ab57f5489bb9ebb6450cb27f4efe0cfb466144e GIT binary patch literal 637 zcmV-@0)qXCP)@|2i2MUNJEAGBA`g{QJZ3ub1IpEW7h{~Z4K zJ3#Qa=Q1XS-;5h0el2+U>(Te$zyAID&HDTIKad810Ad2W{jW5`@9y)AKr^5J{4Kqa z;cw*UKQW)B)-pW&@%z{RUqB7N{{H&&_Ycr?fB*vd;rG8kGw%JlqX`uK!_D~nn&&T_ z*-Q+-7;nUX=6mt$`7e;3-;BTi{QC{m01!YRC;j>JdmqEEKMa4Icz%Tm{+4F_^}h({ z_1{sye%WyRp84|E^4Gur0Kxx1e;6150*D2Oe>41%=Kmef^V^IA7&yOx!2%AYU;o*D z%dq`!;`!w)_PhDb-(PS30!;@9Adn3rpZ_$9NHVegX88Y?;V;N+#{WPzFy?-P;*ar< zJ?CFrnZE^h{{CWM00h;Fvzl@K2fHp9I6dqaaxb00=Mu XLcuQ~?TP?t00000NkvXXu0mjf`7udf literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ge.png b/Mage.Client/src/main/resources/flags/ge.png new file mode 100644 index 0000000000000000000000000000000000000000..728d97078df1d07241ae605dff2f2cac463be72e GIT binary patch literal 594 zcmV-Y0^8x|9^h-OG^F+g7@$L?BC01YQ`Wbb?43;K=s|G$6#bNnb!1Cx>Qe>QfY2m>?Izi&U8 z1o&Rm*8l_%%a6%3nS?}u4*37)&;Q?l7=Q-<`}?1Z>;KQLY|KmGQ@ZWEch0Jnt zUmm{%2p|@g=ujpTGX@n^21dqzKYxO4`1a@2NuYivJ4XgKw*UYBFf%g!{qd7YP>5~& zE`R_4F#yj00OjT7{QUg;`}^~|xBB|}`T6Q!vcs262Lz;t$n|1+qbnVARhhy8{z5C(*C%JTg?tEV3%;s64O@&5h$(1-*>2%Ak`A87BFlP7^( gh&l)WvH=1N0MfQja}g1cO8@`>07*qoM6N<$g4hNuZ2$lO literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/gf.png b/Mage.Client/src/main/resources/flags/gf.png new file mode 100644 index 0000000000000000000000000000000000000000..8332c4ec23c853944c29b02d7b32a88033f48a71 GIT binary patch literal 545 zcmV++0^a?JP)lgG%);U`26kn-@hOg zU%!6+4+cOs(0HIde9xZz`}Onxub&LUB0x(30+2WcIRJn#2ut|?gWYu1Cf+!-K%B8# zdf?1WA}#uZ8oj7u>$I1i0Al&`=O0k%-@icgAIJnM0xA6maSq6BK-ECw|NZ*S`0Lj% z1_pot6puj;05Ax`F!=umqj7^frO?t|3^&I1kxUq9yECc+jQpY84SWH_0#pxl$?v~F z@*hy-KN0|X07U)z`4{NpU%#2aHUI<=%a31wK(7Du52Oc(|3O^?R1IN+RRjI-n*kVB z3=9AP#PZ|EACPLGJ%9cJNh|>9B%spYzZw7h1%?tp0I_@ndg9MNE>313@6R75NcceF zkr51-#U+7;F#`Sf7i0rK0I_`g_NQ&ZRCwBA z{Lg>@|4`Xj5kLT%`al?B=W5I`(ov;U*021*0XgD3^5{teN< z@cTDV13&-@;@|`T5QYI@3O)ok?1DO<2trehc#kXh!0Z4iC6of!=I9L4Jz5Qk(jP`l zJOKo8(qFLXAF#IH8`u5XwDI@PAHNy@|4L4RsD^0x1N0+605O4m05bkR14QCiMDQ;; z>0h$aKjWi;+@CNFzZm}i25JBZAQt8_hOB_!_dovn^Y72^zrTL{{r&6TuiuWpfB*e$ zwD}j1{Ph<^0%eu?|D0`P00`k|9}4iHT+`$2p~p=WCoxfpgkZGj{YEt g{DC2GLI4Ob02tU}a;hkw5&!@I07*qoM6N<$g4!w08~^|S literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/gi.png b/Mage.Client/src/main/resources/flags/gi.png new file mode 100644 index 0000000000000000000000000000000000000000..e76797f62fedcbfca8c83c51951680d6a6e9081f GIT binary patch literal 463 zcmV;=0WkiFP)VoB37QQ+R{;bN6GSUi z+kb{HA3p-oUOth}-@kwP{0U71P%%INK{Y@H82)oDp3V0Dt?T`39Pi$;RTl%zL?{P4 z2_S%&kW~Z0x6qjPzkeV9`s>}VU!Q7P|1&Wm)PrpR2q2b!5Hle5FfeebsWZ)5spT9vc*+3O2}Iw&|Nr{sqx*C2&8&?7c>c4n{QvVC zD9-TjFQbI?+i42`0*K|`>)%Y*uQL4o{r~rGhChE9{`~$Pz`@Ka$@uLpb; literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/gl.png b/Mage.Client/src/main/resources/flags/gl.png new file mode 100644 index 0000000000000000000000000000000000000000..ef12a73bf9628ff5a67b81bd980d9c5d2b2c0f05 GIT binary patch literal 470 zcmV;{0V)28P)J{teTOL@z0+>00G1VRSiT77W_YWkm2*^|KGm- zfAHXcOY8ruSJ7+$Itd_vn4oTd_U!+mLkz$F{Qvdq|L@-*^6S_C%a&nk00990)`(*=-xesBS%qG0|gf?f~#iu^9N|j9|i`10Ac}ZUz>% literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/gm.png b/Mage.Client/src/main/resources/flags/gm.png new file mode 100644 index 0000000000000000000000000000000000000000..0720b667aff506d7892c5c301af04e6bbf932751 GIT binary patch literal 493 zcmVwRhhvIeu00_fCKU~B)yH$s9sXS^B!W{?M(W&}hPbMwO z;*cg65E@7haJ!!XVgYOW|Le(9kkY?@fpY);{sqc`6amR!K*q2CzkUI^Y_hUI(*XjA zMdSH%VNp?r|Ns620Z1T|}fB<6Q zlw#oF`Oo_sVk+2%KTsoq3?TP6gz@)3Ki_`_=6?VI#CZSdeQ9y&f57m8xf%uh13`xW zAjrhTbmsgSfB<4-$Y)3kNW1sx-tWJ^f#}!YUqA$5fJva>FJQR-`S({vK;>fVMSuWe z0mcW=Ig;FxKxv@ppTFP`1*!N0BL9M&0|dYz`1hCL7Xv^5F*2kxF#KQuvOqEU3km&! jiTr^fV1zR<00bBS-TrJ5MX@2w00000NkvXXu0mjfGz`_@ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/gn.png b/Mage.Client/src/main/resources/flags/gn.png new file mode 100644 index 0000000000000000000000000000000000000000..ea660b01faefde01ad2527a6abcf7d1a5c1b0526 GIT binary patch literal 480 zcmV<60U!Q}P)@|A6>41A`El{SSoRd}EL3@|NdrR`1}9=ZwUJP z@AvHwzkdDu1yn7|BMY<#Ab?oFX8(t({tZ$6>;L~hU=2XVuU~(E|N0Bk07O6y00G1T zbT=bV^`Afg|NLS2{ReI~M8m&-NE-fuGynt;*hzmtW+Q3%1=j#1fvO=I{`~y|)Bq4b zU?=?r84r{KY4``%041R|`~%zYhXEjfz)k`h|LYgXRlk0r+3@c_)IERx{rUUv4^RU@ z0D&|xgN*;p0Mzyy>QQ8EKn=iP{qyfH5CNS85I`UeOuzpyNJ@hA{P_#yFfjaPWc&?| zr{By>f0X$D{QV0G@4r9|{}=!Qi18~pg5ikaD#Jf9Xfy-Svu_Nh0nj)GNi#731Q-A_ W8E1tdJ(&;y0000P)fLk0D%}*I7ff3uKv?i+N*~ULWZ>4 zW5%k%a3T{@*`z6pma6eF$JtK+F@C*&o=d^t|Ns9GOCXH@*Z?CVuP#7nB5Y|1kmu|NQ^=?>FP0zrYX%2q2aZH;)P`n*#-K1r9WbfYOYN z??RUX1P~*`M}`*mir*mb{sxCVG>rbhqT(MY2L1y54rHu+wi6(L7#SX-$0bVa{(;3h egu%oB5MTiLH(5{VMZMqv0000u-`~Ig{`~p> z=MRwl_xtx>F!}G#@4vq{&D;bKKrBFA+{}CzK0Nsg1pog2{{I_D14*DX1pWH^3y6RW zSzcL&Zwvqd#Pa7K10w^wlkmIISh(4kI3GTJhN2$mzJLD! z0*D0|DC|s(0(}1j8UFtJ4HA_S5@P=M@9)2VV#30}-~k05FvNkXnV5ck`2-L^EDTKl zn1259n3DG7^QXUm{{H**3#f!E|`n6Mz6>fhdBj29g(UfB*aM-=Dw#|NaG$fByXc1LXhxFC@THKjjKQ05P&# zA9gbr+SsEBRPB^?1!&T?30hEBFhHFGv5AR^>DH}B00G4E=NHV45I6k$@0N4rAH*g9 z{zDN+_&*OP%Y{RC0Ro8e#fvv0A_7PTA~XKMG0?q08}8kE2oOLl>koag&}IJi^WT4% zN&g{c!yE%t3}J9_Fdy0V1t5S}4xV|TB*XjR%dfvcU;YDm6wdeu;Q~GU4JP)J`S$qz^!f=A3G?{>83*rF;63vB|NHv-`~3j>`uzF%_#GV;x3_Tu z05Jg0{{&$2KZ#9J4f5RdzRdzC6Ae5p=(o+T{uB7y^ZNSwN=h$cVmJ#62-nx+ob%l!TKAE1V~I16T$|E1+i#l`u&y!03t z7ytqQF#yj01Tv_Ick58z;rzG}_y7F-%)*RRAv1X@1^f8;`}+eA4+yBm#PKA>w6y_L zQ&a!|00ICp0M7peY0>I$KneW(?7rp&{QCm?`vUdU)NRiG`uYI-`}gbY@;Dai8vP{| z4h7uY=>Px#0*Hx$0pxvffc*LW3+PFp{}_J#0ty11^n2!vLjod>mX_Jy|Cz2{eHy3% zAb?mH7=HZ$2N_TWSP@V&gaITO7A`uvc=2f<_uIFxDk_pd4FCZI)bI_+mz3lOl7E0f z{^u`PGlT&Y`3GeD{rm6lUtS;y)Bq4bz=+dkVE6#Ehk@Y-82x6z3jhKP0OI&0DF;s+ Q-T(jq07*qoM6N<$f)`^cRsaA1 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/gt.png b/Mage.Client/src/main/resources/flags/gt.png new file mode 100644 index 0000000000000000000000000000000000000000..c43a70d36424b66f1627216ad988cd23a4be9285 GIT binary patch literal 493 zcmV|9}7f|M~m>&%ghRD!%{(5DNnfNcHoNKp}=7 ze;EG#|IZ9j4hBF)cVB@t{Qmo2T96TF4?qC109^ty;2+Qi2B0xObN{1)Uw^{8{`KJ4gZ0H zP(J{D2s9NSfWR7{27t^!x8NVhN&g{E`Ueb#e*ggla?+pwj3Cv27=VUhwE^V&zaVoN z82$hR5DUYve}Dck14V%vK+Z)23?LgAK*7ZT@-zbjKmf4-BkT7+CNVw+pd~+kF)%WM zL>VCz0|PT7gS-eZm>Gd?16jcE4PH%g~!@=<9&!2x_@aEGW jS9>vVD)6R*AQp(xFW-Wt{{mJ2|NHk};Jc-)O#kPzME?5A`1{ZQ-#|To{!0im{$XGM z2p}eq*?*y`{{RjC%V7Pj<(}hvo^Z>=hrJ-xK=d0#0&M^~2_S%&fR6tAp8=>2$p8C? z0q7F5H=q9h`}OesIT^OczkdG%sRkpU6i@>|0I@Lq1-k@j0La}yZU2}V|JX_T{{Q!% z>EEtDfBydd!vuBJUq+w?fB<6o2X+#W53~WS;s2M<|9d72Yseq)`11cZJHs!aS-+wF z1}X*!ASNLG{ST%Ze}QTk7ytqY@{}>p)on&BOV)*xm;om>72$%sP!HhqD7ytr@1teTu4J7{m`^Wh2AGglm{~UiA z82u;s5_9PZ|FH1)~3de={)r1*?FgUtsi`kx>?C4?qC1 zfX)8@|LZe*d2K>lZN0{{I4*^b@QIr~;(tA5`a`e}Ddhy$ldQAPvkwr9kh3`~WiS7Zcc2 zh-#qDKOj^7{QC<;3||-k0tlpm>GvN7NlB2NKYyjTe{-?^h8PLd@aHekus?tQg2Ee! zfN=^CK#X6(5e!Gd)(i|h;JEn(j5jcXFhHq*fkB7?Aiw}&uW^ngBcx#f0000J&k9ol;AaCAG*Vvs6lsG2f+AJUecp&K4&zS7@MzJZZ+RCHJO2~-cn~)8*ZB# z%#~(Seaqctb3On>xdArM!+zLfe2=iS%3k1HK82I)yo62#|&;D2*%o~N(LQ$HrxFU=@<#wgQDty7s|5?>qxBTrc>UoBZ!}1le z#)a`Pq~$aEPO=D0fO80I7h5SSMqU=q48*j9Qb*%7#+Pi|ervSf?0bSFwKsAPn1FO| zKH_&kh#AJmvOUSnl~!1AmcaNJM5awz`0DF46>zWZuCh$z(7uBp0to4w2iu-uj zV9oc#M;CkJ!OT_8;~(;r&Cw`0K3r=(%@VWyiIA#;S}+n)^}q>|)QZ|IaYyyY!;frq z6mATysX~aM!z!n$rJ$=27fpoIr3iB{q|Gr32uDRa3PcNj==OQGHve|07^1DbtUgzuEQ=j%rDF literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/hk.png b/Mage.Client/src/main/resources/flags/hk.png new file mode 100644 index 0000000000000000000000000000000000000000..d5c380ca9d84d30674f05b95c2f645b500626c07 GIT binary patch literal 527 zcmV+q0`UEbP)00;JD`K-EmLvOuK( z0R&e6?>|)a-@i;iz|8zVIqAQ;I)|_@BNM~FU%wy-s0ZjAfB<3vx(uZH&mV?Ae;64V zIcsYEzkmP#{)7J;N}0Ju0muUq%~ z^Jl0Zz)k`PASO^y0(FCg{s2v4J zf4+RlSW)rg;lp2_Kl2?q^5yYkpazCNzyJOD%k=jzP%%INf#Tuc?>~%^l1w0DfWH6z z1E^V4lvz;l%d1x`a&jQQ{ROE8h7C|LKmaj5WMKG(8n4KVKd5of#=rm&U;y%qJ?5>3 RVzdAN002ovPDHLkV1mTk^F06n literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/hm.png b/Mage.Client/src/main/resources/flags/hm.png new file mode 100644 index 0000000000000000000000000000000000000000..a01389a745d51e16b01a9dc0a707572564a17625 GIT binary patch literal 673 zcmV;S0$%-zP)>fJ3En$GhGS>sbE%%m3$AD)q?8M9y>88-}kR7#RKlk!P~Y_PLuF7~U~3`~nC7 zF#yj00ZUDdpLsm{7ajP|&HwoK0Usg|6%f4L_{`Mi{rvv-`ukf=Ed&Gs-sA7L!Q7*a zj{*QO0M7pb%?Sw^g@yy{>ihEY{`vU@3=8@G0rvO$i3mOL`~mv-`W+b$Mmr&io5dg< z5v!7q0*L95jt`TzK8Kd(Utv)OSp_aLv){6ccV+Z`{Q2+asKUU&aO3`Kpz6wW8wp`< z28M3{0mSqnB#A*-c*8%1=RD#sSOwMznKA3=e&iEzwo{cA=PgXK`2OQ}gqId83!|%* zA_Kz@fB*n70M7pdECCwp4H&@R`1|(w-}M5x*74i)0}%fAt;XafA{48))#>Z>?CD#}*e}Ret0tl$#*RMZ7}Jl7Z|M45`5*URzH9L z{rmSnPy;{!u>dsyO%meg+00000NkvXX Hu0mjfN{&}S literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/hn.png b/Mage.Client/src/main/resources/flags/hn.png new file mode 100644 index 0000000000000000000000000000000000000000..96f838859fd2aed975f5f4134050fdbc0486ce1e GIT binary patch literal 537 zcmV+!0_OdRP)yNpn^YtfB*U? zE6K^g@B<)#SlTBTcsfda`|@_0R#{e$UZ3l|Ic6l=B#}T zWCl5lg}RFY8S^$g{qgfJOdc2ve*glAv3c@IFK6|y-~NDH^$#cn3{a5k!L9^_5>O@B z$^W^zSlTD;0tg^R28Q0WdbfK|zW)9V43odV{`~*->+kR1AO=tbO#T4}-G3E1?u#4x z0Ro5x7#++k42m+GppXWk{}2W^;6Y*k7i(1vOT1`b$6{=&w9#5#oJ b00=Mu*}Zhb7k&Za00000NkvXXu0mjfKokPk literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/hr.png b/Mage.Client/src/main/resources/flags/hr.png new file mode 100644 index 0000000000000000000000000000000000000000..696b515460ddb670acb7e9de4438aaf21fc5fb77 GIT binary patch literal 524 zcmV+n0`vWeP)@|4`Xj5kLU3fF!G{fyDoR{}_Q36Vv~H|Ns5{^Z)NZrr*Dqe*gae=g)r_`DNuw zfB*t(VEF(4$y0{Ee}M=nS{mB(NB;kxJOBSE{F_{n`2Y8>|G$0##TjK~fi?gH5EIz! z|8Uj6|NiY-l-<0|OxA@1MWlzkmOI;leLR z$De1;{4g{7^y$;LZ{NOt{rct07a;lk`E!5(f@%OM1_PkhC8`eE9egqWbga z&p;hdpFRZ$Adm)#>fe8W4*Ct02B|hR1*-n0rS<;(dx&bFRY1n$$BzL5h>;=uaC^Mc z(+@v?|NZ~x@4w%F|9yUZW7+yTpo@LH>e(bUfFghX{rmIxzpNnpimU?w0mQ=a>kp9s z8>AGffmuv7DrFgv!3yU7{`2q8f1ngFoPlxn4AoS-S!;e2821p2q{((sbfB+=MK@k8U5Cg$|VB~~? z2XKWZk_lAZtGhi{|56nPieMKY$Bq=4KgZ0muK;2JYWn}5;nka8K-GUCa!{rJenZIL z|9<}gF~mh#ftCOS5DU<%|Ns8~1)2?0{RgZLWF&|Ls)lL+iU2hL1Q5&LKMX(>AUTM^ zNU9+S#0FXN@8@rz^Zx+^5DWL07wmsTIe-5EX@IBzTJ`52%kO`z5F362$-h7b*KaNc zh6exv#P}EJiR%3Sk01R1^NZmZ(C**=fB*Xb3rzn04HN{CU^bJS()(Sf00M~R4FdxY z(0f3MKYtkh0!g5OAQFsz{e$TF`x|H}%fCO*7#IKo2o$W~FaxWA8VofRr202h8w1#j zz=!|{Ah3qte;CCj89_$={rBfLBSbS$5J>(7`GW}-*g)q41Q6q6a2)=FMdm+9l%onl dL?8elzyJ+{hsuy4pm6{I002ovPDHLkV1hP90PyYjz{{0J*12TXlP$`i71!VmC|LYf!%PK1iv=Z0I@Lq zVgLrRB#$I8Q2qeT`3KSlX8!?(3s3+U9e@9T1Mx3N13&;VGFUSJ^?=Y13Wr{r~&-|6d^ahvDC! z|G)qK`}^nrA0Ybu|2K$nd)6X=0AgWa1{?O`IRi-PU$8V7{r&w9sOb0ae<0Pr|Nr{M zEF}%J0U&@_82&N|Y=e#y>zg27mx!0Xgp5*S}vr z{r~fq0csvl>92pk!P<5VDfB!NI2#TFQ3lKnzfB%Al=06ZHfFW+c#KiRe{d<4_V)^&)A0s0pNIe5S)eu>r zF8~6F38(?TQZ#J<0R*xEXct5e0}KG|WIzExE=U%r7$AT^8h-rv@ecwRzz_$3Xaxu` Y0RLik?wUgPu>b%707*qoM6N<$f;0ZTz5oCK literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ie.png b/Mage.Client/src/main/resources/flags/ie.png new file mode 100644 index 0000000000000000000000000000000000000000..26baa31e182ddd14106e67de1ac092a7da8e4899 GIT binary patch literal 481 zcmV<70UrK|P)1Ab?mHSU}=WzCQi??=KL1`SXRBmG?g! zeExSVgb4YXfjasA0Ybs`#&c5^XvcLUqDM3{{9AP00d^^H2e48-+%sM)d02u=%hct8G!N( z3;+QH((o5-_OE}xfO;@2_y=+i*h!3FCjkTyNW*WSt$#tPfB*dj3@CIxKqoQ$2DuvO z1O^6x00KJ+r1UogVe!Ksu!etsL5P6?Ab?navG)7lA4zUWkT?GPWdcP410y3N0|YR! zFo-FE!v&-P=p=vuVq_>~VE6=zV^DnmVAx)=U5ZNz6vaS)0m(NHWW2-wfs+9Q00bBS XO2cxg3=*#z00000NkvXXu0mjf|9Z^l literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/il.png b/Mage.Client/src/main/resources/flags/il.png new file mode 100644 index 0000000000000000000000000000000000000000..2ca772d0b79b255872cde2fb29060bbbbad950f2 GIT binary patch literal 431 zcmV;g0Z{&lP)WlqUuh`uiUU82D1+EBLb>EWz|Nj3k zj6%@>aVJ0ku|Ql5RsEk~{?`9D9{=ZO{V&1vKX2lHHJSgJ0SFC1p8y096I?Y|?05b{XcgblKTHZ zfByjpAQrd=h&?HOAa>`R|6Hv9XB30N3RxDY7$AV4en1PH(j<7uAT&Tc4G=&q@-F{c z8i9e$01Rv(35=ybe;NM%WdxES!M~uG0dj%y@b5pvikg1_0mOLw_HE>d#AF}?ph|!M Z0{|%qc@l5wel7q2002ovPDHLkV1m6PxaI%= literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/in.png b/Mage.Client/src/main/resources/flags/in.png new file mode 100644 index 0000000000000000000000000000000000000000..e4d7e81a98d705da8d7054e77e7d311805659678 GIT binary patch literal 503 zcmVl^KlW*80IEmzVa(K3*_6 zG7fg0I9Zj&0woGah`r_&Kwu3FK=xChQigwjfh>?7kc!_h@)sEWW@MKI+5iwhEdRtz z89B8WSj7JS|MwrL=l|b3uZ7Osk^B4auaUxSRgtG4v;Y11_x}$gi|9Y8?EnG9`1|i) zCPv2p|ADsrhuF4k`@Nr^zUpfTpS$xp!A}Wj4A3Yb2~_s}<0pUsVqyY2p8>1`g1&zJ zsVvR4Yya)fUw{4wtNss>0tLxGfB<5GmnRH!zxweH8<(Mq0N7e&^ba6_ z7#WHgIs!VLeti1p-=9B!fB*jb=l8$ge}LrQ-#`%%`S%Y9{re-sFSERHIY0ohF#KVF z2*K4Ml>Ykz*ZJq)UtlmW{9*tIAQm77@e!0?Mfa}mS!7%=x2KmY(S z0M7pbTSu9imq`K?75n7n_u%3MB_szB3#hl|A07@E6$<6-<>30S5egmg@cSkZL+Ix9 z0st`p&i@3O7{&wE8wU3C1oia^`T7CwI=ckcau@Q71iA1L?!*;{7^Nm*&$?GLJd|NjPb3=0E0KfCb4 z#nAu(05Jg0{{-+tQ26`)!^rI-{P_e371QGlF%9qW_x;Sb;QRdk{r*xJJ*zy?_rWgu z&$=$1lYjzR(%pFe+p|Ni$IOoG{eTR0XemVUia$~;LXXZeDO z0096o0M7peXKyl1N+90g@dyY5ARP|N&gc{s2^<#+#>?j*9u48*^A9U5-*(6(kqBPq zC^|QM0*Gbi#3NFYyicBf1{wyk;Wx-spzFb0kX0fvuWZ`CzyHEKd)}3G%ew&r05Jg0 z{{dZPvvzMc=Kqyj=IHYZ2nGGUe(f)J7ZwZK+v5TV s=!a5B6v9X#`iH^&14cjw13-WQ0BQ>oQ(TIK+W-In07*qoM6N<$f`&OQ@|4`Xj5kLT%`al?B=W5P+gNxB&nJfgpGfTOih;e`=>T z5jZ8;?_>v5xi(~iU^udv!6f5$jpNVh2O?$m1OPDr&i?@Y{r&#_{`vm=wBfh>{Qda+ z{Pz3$!R5rm=Ee2+`0e-d?)LGV)1LD5^!4@i=jZ1F2;wA$U5|F$_;B;f&rf1p(qbG! zCte(9VPa-y5lGpbZn4ZFhzcT9$vfdqmuG z@j$ZG>u9())mkwqmYHSd7eFi*FJ3%$?AX0~_kM%HFED^GKqQ#;=g)7T_f%9=fX)F3 zAdr)QMoCIaf{X{6{|BNG$o>N%f#5F;02KoS5XlH2zyJ$0KZ{``H1_}i002ovPDHLk FV1nFR>VE(L literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ir.png b/Mage.Client/src/main/resources/flags/ir.png new file mode 100644 index 0000000000000000000000000000000000000000..c5fd136aee534ecb59914e336cad18d18ead2a4a GIT binary patch literal 512 zcmV+b0{{JqP)r;gUH{1e{Y(x2_S%2fSMQ?7@vH7`tSc=xS~J*|Ni>>`_JFszyAFKs{8d9NdA)L zm1AIH00;mv0M7pew_3Ln1`-ek5ajjb8VVZW^Whu|9pCfc910uY_2L}~8{YEX9t$4Z z@!Kj9D)d(L0*LYN-@lBEj6f&-|Nox>4F7-s`Ty{t|Ns8~x3>Pz!S){pfXY67`UDU_ zOc38f#US*GW&hv2{?Eqpf6;>f$N=n5fB<4bR}BO)G5?=F{eR-b|HMQT_5c3^H2?$< zb1geNgNn-kGiMln{`!CM;{TsNL8PAke-;*?JV+Z*eYOvhv$==KunT1sF?AKYlWZiGf7_{AKv_o8k9wMiBcC z1B3*kzkfmK*Ds)AfB<6r3XWMgVnF4hNdW;sfB^vU;z%SnI0)(h00004s{hykP}!$Xp8x`g@iqg4NJaUd z$B+Mm%>4cD_iu*Zzrl=O|9^qF|9<`Y547mdFIIVlOMCYL1Q5sui18rvfj0Ph3vJwt z)dnUeruXmP0|XEYv&@95W{1bGfWG{@sKWZ+FOVO6s`df7U=M&0& zKrFzJ{`2=AL>j0Rra@ocD)4hB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrIztFq(O~IEGZ*N=lh=;=qSyMwWku z27eMOW_|f0uQeek^e_9g7KH|eq{JVG0^UaP2Jy4}|L^JL_3uCblfVD@mnS9t`~UCn z|MT(+KT|KOfz%~1cG~~^_2vEk{SFNQ?Cjg~|NsBWy<%2im{vp^YdeS=hk-Cyo6U`Hgs{l4FR(w#{P`G0;a3G2@YSmzI~fu(ZE1> L{an^LB{Ts58L6#6 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/jm.png b/Mage.Client/src/main/resources/flags/jm.png new file mode 100644 index 0000000000000000000000000000000000000000..7be119e03d203695325568174b72522124bb2f12 GIT binary patch literal 637 zcmV-@0)qXCP){QLU}0{l%6`#}@@_VD{9F|q;xF#yj01gpgWJ+A*m zGUYZW{T>4SpXmF{^Zon(`}_X;`}_MY3j1pm`Wy}V&B*(tydCWT00ICp0M7pd0000m zGd#J&@cQ@tG$8vu6a54J`qTCN{{H)06Z&u*`VIU0o2UAMn)~_v4c^|~0*D2u;qTwS zKYspMz5CDEtAAp>e+Mf5)@1$t_wR2Fu3uNK{_0!&`}CDxK-+|V{{|`s2p}dP{`2SW zZ!oxe=XdSY-}fK=Qse!l!T0O_!(X}WAk`4?=MOLh7ytr@32Xz9{pZ*3U(#Z~DiVIl zOa8j^;n%JAKjo!$j^Xni)Y$z6WB&k?3JlvV7}WR}00ImE Xyv9Bjb9W)}00000NkvXXu0mjf@Xt#6 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/jo.png b/Mage.Client/src/main/resources/flags/jo.png new file mode 100644 index 0000000000000000000000000000000000000000..11bd4972b6d5f134045d4e8ce134601ea9b5654f GIT binary patch literal 473 zcmV;~0Ve*5P)M00|Ni~>`!`VeZ#eh`f&K#;fdS|pfB*tJ>FU-06DIsWc#z@uumAs9{`&>~&q~MQB#;&cfB*tH z31sk|Jq+K!KjiuK-^&B5YLK~LCjkTy3s3{|pFco7yH$Tr@L>D>cm1y|D}MvS>F@7f ze}Db{_vg<)5c|)+zsmedM_Y~p1Q1BWd$vDo!X?isvq}Pk|KA^w>VH5L!1(y{_x~TD z9$-NK{r~sxzrPHB7ytr@v6F$JJdlAwh=Ji34E;f3{DCq4fk_4ifB*vkxQ1J~H9>i| P00000NkvXXu0mjf0T$ba literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/jp.png b/Mage.Client/src/main/resources/flags/jp.png new file mode 100644 index 0000000000000000000000000000000000000000..325fbad3ffd3075a4a84d8d898ad26ef7d3e0d56 GIT binary patch literal 420 zcmV;V0bBlwP)9whYk?f=!Q|Ns8||JN@lTD;`{R1ZWk|EGa3dAO8ObDh3E3Cb;oH_5X#1 z|NHy@|M?558b}5Q|Cf`4hZv9q2p|@?lb|{i68>{>{ol0lx`{mi O0000=G`P)0NEt6k^VGA)9E1hT9ocRoN>wSfaWv)?-raRm?)Slj<6Po6w} z{P+i;{`~nD6~!ec29@~tkBNo($fnHz0mS%{fq}QS{_m4#|Ns2?|K~SQUHy-* z`HU>AfB)8feAoK(-@hL}|Nr_0bQ_Dj+^xMk00K}f2RQ&hFc1JYLgN3=lsJ)M+p3uR zWC5ydAM*!ly4H1hsiEFvIU^1)HH_GYz!QMsIYt5i1c4ZMLC4DfztdU-q)WGxxg;|L zi3%uAJ2fm~N8&I2%AdL;`{4^9#;e!&D=C-&Lk8;9|NlO`c*HPy9@C${KeOWnB;`P2 zATRu9VP-jSWFD-S8kh{*zoh2aO#pT8A2W0o(w3 z|5bef!~)j#|KF3RAf-U``!@sYUq;#A42-}3UHtv;%kTfcfBpOQ3nmTE<|1vNq z0ns0zZx{dq2xP;5h!=kY&G_~A;V*`-zy9<8Vi5ZI|I4p`PkusG11$kMn1KNxfWUtE z{TpHc!>?a|&i!W8{>5bQ`~TnHf3N=fz5nHq%!z>% literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/kh.png b/Mage.Client/src/main/resources/flags/kh.png new file mode 100644 index 0000000000000000000000000000000000000000..30f6bb1b9b6c5bf355f67a17531fa73beafa6639 GIT binary patch literal 549 zcmV+=0^0qFP)P;@arD~1pHxO_zPr1&>t9y%wPZrAQpyS3=Ms1K-T|%K*j%o>i_=z2W0&D^Y8ax zhQELQ{rLl7|Ns5_-|C4+00M}Gf#D0s|6k8u{RAokD*W^JKSaeZAp18+HBcQ8{rdA) zTAYJ{;SE3lu^j&CtN66?*W<_k{(=kvTJiVSPc{h&pyuy)ZZrJ(`}gOM|G$0#rP$=; zY#H_d1P~L*>3>1SGXDMzbOE=49-9EaM0J&9T`emwH;=g~P!Ocy*DnU30tNVGcpOKXK;IXGH`G*aB%Pig@_cF{AXeP^XnG~{rU5QfdL?ZSim;?VE{Sy z7c(ax6CWR+s|%BWAkauYPfsQR0VXz9sPSMM00M{!7+@fO0fqklmFn#S3Nf;>{s#gU z7DjgV{|pRrOO`MKef9?wUO?vn1P~(w!@{x_lZQ{f0d@UhVEX<0FF08K{sNMJKnNIa zzrX(mdR{@6d*A9+009Ja65pra?7SkZU^!3-{)PrTC`>`Y0b%_6|LHH#J`sQb0@|>a n0T_AEh(trkF%3aX009O7j5IT?Rho+J00000NkvXXu0mjf2r}#E literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ki.png b/Mage.Client/src/main/resources/flags/ki.png new file mode 100644 index 0000000000000000000000000000000000000000..2dcce4b33ffe1f40d490cb1a2e03efe22ea56155 GIT binary patch literal 656 zcmV;B0&o3^P)8t@U|NsC0_uv12fBrK4XN%za&+zB_ z{P)aXfBpaa=ii?{|9}7d|NHmt1seeZhy|?e|DWei8UFwK|L5=jKYtkh{P{kK2}m(L z`||tm?|;94|Ns5#-_IY+QnEm+00M{!r2OxHhJR4iK=k+TZ>A77)*#lue}Db^^$Tb= zko*TE|NI8J3LpSOb8Z9x2m%4nC$a{*Gqe90@enzQH`ta%tA$7d{Sv5iP&~x@8dMF~ z(;dfh&fyCHF#yj00oRR-9sTeJMNR(n^YxvS3U`G57YhIQc>VqR{rPSM@(uBDU=5F+ z{|5&5hj_*A{sI6o0M7pbe|x2IYDAmA_)R$v7XIy^!u#*~18PtP{r&xWnDPVw`hiji zey{rM^8J)$5m8%@0*JA9=57`qRaXnayHCFDJ@r1pR}vUfQ&&Fu_xE3Vu+-%{U$!58 zQy3!)avcufO?mzv0@a%gl z1HS+N7!IC&H?5`#AOHX{0M7pb00(zPR4BWp;5AJa{{8^XwcY>#`eSh{`1tyzm&^bF q{8L{tuE6I1;oe7AG|b!z0t^7P6ga05`yJ%~00001r;P)}L!W`0l>rF;|7So3KrTcC!ho;=0*Gbf0S0@;YBwbYEs=i=3_$ev|Np-X41fOr z{{tp}0~v7g%iT=?0mQ<{9!@# z-(SD~{`vL)_wRqdfBpaan?+XX#@15+0mRIp%kY{1qrJR?xs-YLhQhSVzk_f6;`sgh z57Tc3#$ODKzZsZ*F#y?2e^`Y0-&}tV5I|5HD)yDjz7nmS1pU~~aVF#A6wD&YtO00ImETIDprOD_2B P00000NkvXXu0mjfKOhx^ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/kn.png b/Mage.Client/src/main/resources/flags/kn.png new file mode 100644 index 0000000000000000000000000000000000000000..febd5b486f3f90056637b23caa26d838fbadd7d0 GIT binary patch literal 604 zcmV-i0;BzjP)h(K@ANy8uaQvQ^_nWcs z*Z-GL)eL_?t_B7*Kmf7KbznHW;LqhtK<$TZ9ej=*TGLweOZn#S|EVB#AOzI#2dDw) z)4xDJ00a;t5ND<*{rU5ogY7p9)8EGU->gS|Gwk>cG!LX2Y%nmu8NlfEl`8-N#0ZOI q248)K1w0H4M?n#d6+r+%fB^s&Q!OA|2rzyC0000Cs@aq@DuRlQczuzDZ5@KTj2q4CTH~*`MftCON|DS>3-+w6c z9|(gO{~7-O`v>9vKX&5_Kmaj*WME|P@B8=S6~kYUnG7sU|G#_z>G}QV|KC3#^7rq5 ze}4b}^_xjb^7)+E00G1Tlwy4KiiwGVIVgltUY_yi&tI!o|Jl8p;n#15-@icU*KbCk z6Mz3^`1ON{fdL?ZnEnBs@%JyYzyH5mxBfr4|7*t%=AfYeY;6C2{Q{!DKY#uG1wwy- z+}}Xm3;+QH(!lWNFN3HElfM4XRjZk-tp4-xFo=l!{|(XrbkBdVP9XXJ&!0aG3;+Sd z1adCWtuJ2w-n@w=AmGpT?F`?)|9k%Y-_M^QML&N+RfBB+Dh3E3pdT0*fp#rf!j_o$ zUs8faP3>oG?f*A#7{O)(oeuE;(0C>wVqgFWAQqr4|9E)*J$m%5y#o|bz~KAxm4S=v zKP&4$usU!k0b}RSKS4oYgaHH)%c-;9wWL`3={r0|OreK!5=N4TMk7RCwBA z{P_JV0}}Z6?;n_iu%H+Q{s1vR05Jij`8c?M=-GouSI=K${m;n9!7aeW#m~(x$j!^i z%zXLG*~fS9it_V|2?zl-00a;V#NgMjUvJ&I^~3uQB4T37ii-d5-u?gQ&wmw_XD?o^ zUAsm=P*7M%NJdr`Xazt3fo%By|Nn;%AAbM-ZD?YwEG7BBxA%WR!T-FR|8><0Vxn$d zUT@yKdH&)BP#Mq$fB<3y`hl076R7Rgt5;mSyo`Gea7>-}|M%}Nf0%y${3VbO@hKwm zAWK?W z+TGp#<;$01Vv<1pdP-ancZ!Qkd3bmLHK?nrgX5I}Ab=R3zkQ1wk#LIP517FKVgLC9 eRt>}e0R{ktF&Q^6#MUGL0000@P)xg`upqGzh6Lx zERQVE2><~E((v!!|G%IA{@M07EI*V_ln=0RjjdN{oM|h)7EO z{{7?6U#34`ML_iD4-=RKMg|km5|FijfgS(|AV#2u+YAh13=HqUkqe1m1{eb(!T=Kl c0)PMm0G()MDW>>^I{*Lx07*qoM6N<$g4p`a`Tzg` literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ky.png b/Mage.Client/src/main/resources/flags/ky.png new file mode 100644 index 0000000000000000000000000000000000000000..15c5f8e4775b2b68e0360c1f4ff1f37e61611276 GIT binary patch literal 643 zcmV-}0(||6P)0{QUt82Kj15^L?rd`uqE`oNxjFF#yj0 z1e^fI2i-9Q(8&Vs@%;Dq2on+Z{QuwM00sm0@b~}!1OzrH#hk+X+Tt}E6bb+U_yPbi z0M7pcvI;2tA|wYPA^Z*x0300&EGqR875n@A_WS?(`uvdQq*oFTSt1&p=b-!h{Qv;{ z0st`p&i?}PGCBeX39IA)-~tZg`v#`;6$Tz2`uqI%`ThF(|NnA-^wR?L!uSAshx_~g z0000205Jg0{{tif814-hz}E2g`1#%M`@GEN)${-9=jr|Z1Nim(?*qH%B39iMC&AR2 z>ggNf+PVUWW##cF5^jI~{P}h24a1+`jKBZ?zJB$|uU|aB82|la`WF`1`0wBUe?VZ- zRnf8uXahh1u`n?Z4sYX?@pO{;x_8g;0tl$# z2T+})81L`j|Nel$KOpz_zrTNegN!(Tli}h6pb7teeq#K~3=Aa(fB<4-V9 d!wLWb3;+YKCC*ol*cJc)002ovPDHLkV1loTFLeL_ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/kz.png b/Mage.Client/src/main/resources/flags/kz.png new file mode 100644 index 0000000000000000000000000000000000000000..45a8c887424cff6eb0471f5a1535139b965e241e GIT binary patch literal 616 zcmV-u0+;=XP)g01!ZoKn?Fz`<^k#?O_Q1 z`=8<8e}=z6_5c2{{QvRuAM+0emcRf01JR%V|9}7gKjGLHfB<4)U|@L0AiL;yFw=jq zasNSbGXHKG{NMlfpWxpAK41Q^gEaj6FV4=$@arEy05LIu!2gi{Gync$`1$|;-~a#q z|NCw7f0xa__4fbPn*ZDO{onuJ|3JnA^#F|o2q31v4F4ql-&_7K9cVD)|KCjiesKSP ztM&iLe}uy71TZ z|DRv){|H?Bqoe!x=x?wkfBu3(0w91`fMN3Vzue;gQUCt^|MZVr;{RRU{|DCmOS=6} z@%2BU5C8a_|1a6~&;1YB8$b;J0mKBvKpP_e^#OhS_CN2(f86)~DtrE)Yx94t!v9OT z|MTDe69TJd_zQH>zdry01PrBrQvYt3|7!q-C@@&>{!_XBPnqZ6E6@K61pYml`M2uu ze+NdW22lI}1Q5_kK)3wPV4bt}e=;a?{xkdrMUmL?|7pP3c>kYY0b~&4U$AjN34j1% zWLW#P{SRLy(>r)v0s|Y${(}_LK*N79FfcIy1Q-CnX{(%t#68R664G@6GIJN-*24NrwB3rW+v!ydOp^Ef6{n_)(btIFVjHa=pdp6)} zz^!@$h^2tpKUmc4)64h&|Ni?2LVth#{QKwk-@kwU{{Q=j;qSk{fBpc`pWlD@C4}l3 zHUR_xF#yj01d}3g9TFqw{rUX<|NZ>{`TG464+L38761SLpR(uO=J){s0Q&s>`~3bJ z6bIb^kphT`;m^O{e;I!LWBmP>@yDNkj7*H>@iN-VTtHNrD96Ue^ySz8pMMxZ=pRTg zKmaiT)&Bj@@b3@E4ZnZ>72sjw}n?8s2>T{qV)lKMV{%|NQ?241(YPff@h;hz00Cp!a_N2HF7% zr}y9gR8D)C;wJ_Sx5=xYz5MWpi4mv-=yjk&KmiL7K#aZ&_9^w5@1DH=3l15e)xUl~ z-0=7B?|)4HL4sh3f5LL21siGr0*GY=!$H*K#ZR;BJ~dv8zS`wDeeIR3;>1y|KC3hK=RLTAp0Mf zWcc&@%EC-|_DIyI;S5 zZoa(f#*3@};Q;9GfBygi2&4h78pxV*WHQhn|Nk-k{`>pa-{0-~I{v`b{|EZ(4?qC1 zz%@X;G0|`0)6dV-R;K{9rJ5v|%9;KB_nVP{8R7~c2@pVx*BKb3t8)H6dH@UxP=NgY z{qNULVEFv`^^@@rIK+N~gX`}f7I~!;+fM-m5DPFa{(t+%C?E(7W+q^;{`t)a3di3} zzd^yz010JK%>4cT^&8LzfB<5=h#HaqkRlk)Wq^@D01#jR5K~0vg#SK#00000NkvXX Hu0mjf%Ubyh literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/lc.png b/Mage.Client/src/main/resources/flags/lc.png new file mode 100644 index 0000000000000000000000000000000000000000..a47d065541b0d998da832e1981b479097a9b36aa GIT binary patch literal 520 zcmV+j0{8uiP)#-NSZgwz{Qh@GSmETWyIzrrS!pkkqDDw`CEYY|A1Dxfxke?UP9EWUlaZ{K@! zXYTRdfWyFioqIm+xW_M=V(hYvbO?~5ZHkEt37xh?rY1@PqM%`P+i2w@_wXJJO#nzA zheNt~+w1B3H|fqGMx=x!lms6SMy;uqQ4R``NH&49%B_cditsDHI6DHXLNubec}E0~ zb8dy|txA`rTkZMN{rCM3FV`MqlfuwZrz$X1)+?ntF|UvTkD3M?FxSaem74ag}Vzy5sA+`s~GjvDl6>(E?=UGu{=w?r5#MJIwhn?GrT#s zeRSo}v&#TUmcjL&mxEF>2%EIxN+SI=O=izlM$T5JH;yv-C%^zTfK|9CLa`qJ0000< KMNUMnLSTZ|5$YcR literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/li.png b/Mage.Client/src/main/resources/flags/li.png new file mode 100644 index 0000000000000000000000000000000000000000..6469909c013eb9b752ca001694620a229f5792c7 GIT binary patch literal 537 zcmV+!0_OdRP)sI{|q~>>TZ2$qp!tjsj_dmuxH+6SkQv+K1^Dpafp!@&+{r#8W&u@@le*a|z zk$)I|{ss9HAb?mjKkZQc#wYOgBhb-*|NQ;?=l9<~e?U(E{r5MB3DgSYf*4|4g1Z=I z0R#}sfv@v<-|;cLeDMG8um8XQ{Qv#?|LOa4L zz5|hefaLFgf557NB#8Tm`R|`M3=9AP!~zPVKOloaN+E`UP5lQo8*B+s^WVQre?jpA z5I`*dz#16EB$cmd~05Jg0{{#R40Cvt10RRAw z`u+d`{`)-+^5ON`yb$y0|Ni^@{rvm=`~3g>{0I^G8ZGb(2;2e)q~Yi9|KETAVEFqN zi2na)xwe&yo%hS1uZ%yx{Q;WF%<}W+mp@!0-yWP|m)B$h2q2(_|NnsK|6hiGKudoE zUGn9{-`6*oEmQ72y~rUb^YQPmzn@?Idwh=T>wA{p3V#3shzY3p@87?_|NZ&@_cy~| zpmRZP`1kJ@JI^@8AFa{s9OeCZO+ts{j1?4b<=-Nd5zA z`2GL?FD{9%H@9*#{QvEce@o8hC9n7wpg7QIfB<3vS^!iHvfyK-U8V5ZH#le}4S|yXQB^Nt}W|`Q?5-JjKDn&hY;a$Df~n9-WqyG5z}b zHWMS`AAkU20!9tv|KD6JO#c`e|FbePFfi-*z4`Hxjg_16|9{rM|5*PsvHt$a@%k1I zzvO#v;hz8j#Q5o)BT__yf)$AVfxHBa6HpujodJ?%_y>%AV59*A7yy`5b5c`Z!JhyC N002ovPDHLkV1l?nIh6na literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/lr.png b/Mage.Client/src/main/resources/flags/lr.png new file mode 100644 index 0000000000000000000000000000000000000000..89a5bc7e70711575c1ee3b83cc2be7f0e1fb29c5 GIT binary patch literal 466 zcmV;@0WJQCP)2Y|A4{o z-@kwT`t|eY&mTX2eEM}Kp+i!_T03wQQEla((gZiC0cs^;{c3|jAkj>009Kl@aN){ zC);;k0UG-E&);Xo*&wq)roznr3pD=Ezu&)DC1p;}S_BY4jKAN$W)>8Nm;toyKW@Ot z#Ps&Y4S)b*xg;zq)7SR<*)x!NAa?^@4{|ZkY%l|8FPQu15397y$%U%{0*LYZ>zAxx z8}J(slm+_X#f@tK0mO1iR9wET{^!#tU}GSb{Q3uSG}s1+e?a74b~(9Y%Qpf95aZY9 zPuWDo(ENa58%O|%pI^NU5I`*FB&GkLM&}>Ys6}P~0YHEO0B+J}4VS0Fk^lez07*qo IM6N<$g3a05u>b%7 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ls.png b/Mage.Client/src/main/resources/flags/ls.png new file mode 100644 index 0000000000000000000000000000000000000000..33fdef101f74e38e2422bb85dc8a31bbf1da326b GIT binary patch literal 628 zcmV-)0*n2LP)NT^udI6UX~&u_xA`izWBNXkIq%g^6Y%Pu{8|3yZC zlYxl^Ab?nacHcU-^!@XD|Nj5|`r(zVwkHE4BV6^xC+~{3+xxCp=yQ1PA~B|JUd6PoU1p;PCnS`uh0!`Wt}y4#)fi z@&Nb%0tn=Xmv@c{N@*}KFh0M&kIP1>YRmQG4?lbdn);9N*SEXB6{h}Te)Ie9Zy;d! z#Q+chF#yj01pfa18Ye&*C_n-K{_XVnU82h1@caA!0{i;;`V*Y{7}WX#^!xhz{Qms? z{`>&^00Ic4fsvW@|G$6Ruf6C#{P_EiKfi$f`+Dz}{FL7;Z+`vz4fMjFe?Sd?fe5Gp zAb>y`UVr&E@!c&!7K){sO`Ozkh)melY+9 z5KGUl3lpbYV0iHf6xF}JF{n*t;A3C_dhHtn&^!?O1t$N2Nj?UE00RIWZBXJNY9>Gc O0000 zKY#!H`S<7dzuzGA_xCRl`Rmu;Um!M^l;`6=xPSp5fLIuQF#P%V7sv;y25A5(1xW+7 z{Q_w~Xakza@Pz>&fLMUe`uqRSpZ|Y=>VQIE8-4+ehiC(l5cdDyKm=3_5I`(zTN!l! z|Nj0Es0O49Xx6_!5M^MM5b`fj@gGk4KbtNx00a<=83P0Vn?HYFf{Xx4|Nr&tKga?w z11|FC_y0eCSvdcCFfafF5XlYnMRN&=-B{`>{W0U03nA0WvHB!TYz`r< y{$OAL2q4DW;E4VQBmbdt8IZ(*2pDGo0R{jiB6maa(%qQ=0000! literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/lu.png b/Mage.Client/src/main/resources/flags/lu.png new file mode 100644 index 0000000000000000000000000000000000000000..4cabba98ae70837922beadc41453b5f848f03854 GIT binary patch literal 481 zcmV<70UrK|P)?-#?r-wgj45C|ZESQtLMVW?~Zs{a4) zALIXj41fOq2a1uNeOQW%&E= z|DQh$fB%40fEE4z10q3;-;ClCKpOx8h=su~@}N>;ErcnO|V^hXG82+5i4Q+5aFU0|N&GK!5=N X;lz1sunOP500000NkvXXu0mjf*7env literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/lv.png b/Mage.Client/src/main/resources/flags/lv.png new file mode 100644 index 0000000000000000000000000000000000000000..49b69981085ff54568907cd51a56a1e5d8b01ada GIT binary patch literal 465 zcmV;?0WSWDP)TuF);jrk;v#5jAUY900)zv`N|Ns31p}&9s{rUUv@1OsF{`>=? z-@pHYNg(_0@82^wZ2|}&78a1v|Gz$Y3Q`J0Kshi8lm?N%fQ(=Ne*FS+xn*U6mIDM3 z3(!4({{8=rtQsf{G!?8Agn$gN2Dab7KQS->1Q6rPlP7uPKoY|E^ZWmwzrbJwx)~sVSb)*-|Mwp*NlCCVfB*i0>4ZfB zFhYR-gakS;`Tzomv6O+~6D%TsAw}vh)M$o8KMw-~K!5=Nd?C`~#DJkl4QvBtQTT<=hAW5C&pkLRXaMS_oX@ePCublh%7`S0>4^BHJ{X z&jSWU1bzAnAdrUt4F8|~c=q?-U!ZD;3Wy>I`UNEa{sJFg8g4-`_wR00IcC z0c`wlknw+DNZ2$-$7NDlT|NlUo0ap!i1F~k2r~my1Y5)izkcK~h z{{rRU9);Krw*=%9uq;py&^Z7B1lI8X4~o-~jQ<6)85pKOHYf~%iU9%$qyZ=)&LIKv z04P8aRsoF!DgoLL3cdee4gVMb0*H~J5hWs_B!uw~i3^Ex1_pot0|0+0kn{N-xWWJc N002ovPDHLkV1lkWn<4-J literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ma.png b/Mage.Client/src/main/resources/flags/ma.png new file mode 100644 index 0000000000000000000000000000000000000000..f386770280b92a96a02b13032e056c3adfebfa18 GIT binary patch literal 432 zcmV;h0Z;ykP)@|4`Xj5kLT%`al?B=W5I`(ov;U*0{`Ko0*gOW1x?dnY zU=0kve*-lD1P}`lGXhluRs8wG@Eb_}{{H{>S-s!?{`@vR^^5K2FR(pO4M5WY0*DFZ zqCZG(`2G8?)UU4`zrJw%x*-cw4MBhY08Ix7Ah47E{sH^x7s!U+ztwL3`tbkv-#@=J zum1jzWCJ7ENdN%^b`n?!@|4`Xj5kLT%`al?B=W5I`(ov;U*021*0XgD3^5{teN< z@cTDV13&<=05Ky_HBiN$KMcQtBo?#b8i1w)1P}`YD=UMn?0*)P|9^oV_=9jUlG7n1 zgOt?2g9iZui1GF7*Fr)<|ABx33~>V{CZ_AxuLA@S%fEmBAbhajaRP`eP%%INfiyrk z1T_G`pFe*90tjjYTn_{=GBPrt03a7C3lKmc4KH52_yY$2zyM+rgbiXafFO_o^aD@| aAix0StzUbk+v2SN0000M*00(~<{@!wBU}0eR!0_)M!#^$%c|o1wA4mpD0t66HlA)zv9Z3HD2a;wKuKxf3 zKLhV?#{Ykr|9@us&njQ==l`GI|Ns8^_xsQFIm-b82&94GKf}}4zyJRI4@Cd}JZ511 zyk#BtzrVk|eZKww|NG~I-~WGu5Q~)bF9rsH0Ad2#1T-6>`p@704FCVWdiqyE_S>%? zzn?t&`v3p0|G$6zgQ7n`R{;bN6VP4%{xkdmsRoGvjXbc2Q*ib#i+$e?>}LD_@7KRy zzyJOE_4n5=pu>QE00u!g^X8F}1){Re7)$pq2>GzRP>hz5WF0{Q`{ z7%1`&WXbP;Kox)fFb41M1d2p!76A1_RfAN3oCFX+Kn)0M$tdvjY9_8yH^9QVjn@nfZP*{rmI(|3?O9b~d@cVAX$tK?&3V z5I~F!3@gDg2t{v?r~d;6^$&1NgV7;ZP#i-L5C8-i0C2iwRaxXp%>V!Z07*qoM6N<$ Eg62#MXaE2J literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/me.png b/Mage.Client/src/main/resources/flags/me.png new file mode 100644 index 0000000000000000000000000000000000000000..ac7253558ab939481a85cc06dcc4d73503afb9f0 GIT binary patch literal 448 zcmV;x0YCnUP)l$FJ^m&#tWvBA4C(n)b76qu^ z3TjKJi*u=MzAs;^(tHqT@6cZ|8AHxz+0T%zR}I9mkc`8faCz48MN^H2?$<3((yN)j;^`52o304M1f80R+|X9}Iwu*X+N)@cjDs2c+lU z?_dAGBv9wyKfl@e{~Tjr001}1@4rADVDj%Tpgtwu-=}^u z00a=o2DtG+^6#HtKYxRyQB?o^{pa7Gzs!FaUNHa!5W-0i5^f%h1nT_t=O0kRUm#-m z!vGLKP#fTS#5u+P{rv~@0nkXGhChFS;q~V)5d8o97pUPE13&;VK7Rc89~k`k^9M+( vx2Q8b0Y@nl1JFDW`UNKcfk_?)fB*vkB(P&2-J7g<00000NkvXXu0mjfGX%sy literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/mh.png b/Mage.Client/src/main/resources/flags/mh.png new file mode 100644 index 0000000000000000000000000000000000000000..fb523a8c39d40401b9abcfb144a73cbb2d76b286 GIT binary patch literal 628 zcmV-)0*n2LP)qpJCu1p{#SASZ=3QjbPhlO05Jg0{{#T~{M_a8tGL(@4F<^6===Qr`uzX+`vVjI z>RSuFND|!tCprH%UjG&-`Tqfgkh}r_F#yj01OWa1_W1j+!`}P+|NQ*@_xS<*{r*D) z#ao8o+xY#_%t@QS?0~yfBW+Wj{_zyJQv%*f2f%)|)v=^uaq0&4j6{l|Ybrbh`TZ`2ITPyN!~wBya;Q+wXO z|N8qsP{n^H7N*~zApZOB??0dhfB*t%@KJoPW~Y4ZyXB%oU++J<@%!g5F#%3te)iws z==lAQ0jwGr+CT&T00a=DyL0x5XB;1||6pKv0FF^K^bd&vjBEyg00RK!=O6aq+V@KU O0000p}y_ZxVsQQo9l8qD!tQ%&&F2zEbEdU-v3mY$p-gb*;wwp?LFG<9EeNpZLj7Q z>zeacpNZ>XJG@0bcmXcALo;Ad(L@#C92p0~G#aM!FfF0T7^YIJVVFaIl|0gRpSyF_ z@0dgJ{oT}qqUk#4;-a-U9Fej5EJ`tIE9)E!qDfL@GEq>vEDI}q@P8EmenIHxu!*Cc zLK?@%1j7u0A`mPlm`kyT;5daWs;EH!+LV0IWO0Zyj4+sHxRqia#e9ki#cu?^6YQn9 zOfZ8&4uw%r9nR_}FG?tJ1sBpnAsdGMh7pA$Mlm3ABvgz9aj&GrxaT8{1^YB+UzPEM zQQ5*0;(QnblJRNh>E*%16wccXl466KPu9Lk=M%$}%9~Z3xs9na6KbZ+^U;AoQ+JVg=BO3kgY$Vuu?iP6r(sQV=EH;Iwf|hcN2Nd zPl_EfW;$kS3zh=H4ojC&X!7Bd!`~OdX{VxLp z5dC3f{KL%rhe`hT|Cm4jfB*jf2Sk3^vlaWpqpU2@27mx!VE|eEAE^2d5dHlPWd8sE>;L~hU=6?i|N8~f1J=Os z`!`SnKmdWv|I6_A575egAcQa*n+Bj_fB<3vdK75(@4vtQ{Do`yh0yRHtQw*LD9^wE z5I`Uef5B$|`u7W{2T22%M6!Vq>?D8y0%`dD|M#!IAl1MB{`!Yz!@u8fUYwuxex*z#9GlH2{4I3~mO10Ac~g+V6jVB)KI)-uUyEi4o|t-;5vX%A}^sE-TCS?;peW@7xO)zGwLT z>;Lb2Aa%cf{|B;v0lB{!Wo3aj00dxJ4sHN|VGstA{YR$4C2D_2_!cBcz(8l;%*ju; z_5-pDt^fjRVEX=(;rPk#zkV}({q}dw+K<0~Gco-B2UY|%8?FIpIzRw{{P+Lo&pY>j zzJB!i)2Bb*-v4F%#qjU=2Y>(qX<+*Ohe1*jY|LMVKOmiqU?UkIBoO`m3qe4|00G4K6*VFmu*EK13J3rK Z3;;9iRuWt9^;rM_002ovPDHLkV1h$@)rkNA literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/mn.png b/Mage.Client/src/main/resources/flags/mn.png new file mode 100644 index 0000000000000000000000000000000000000000..9396355db45a8ee040c790782209868acaad4b85 GIT binary patch literal 492 zcmV@{}>pU8D26lh`@M2^zAEy;6E4#hyVhJ1te8n&0xjw|I|OmAOC9q@qGTx`1kMs zKYtki{9$JJ&B6L7=Kt^CKt7QCvS|}Q0D(0y{Qoccl;O=^hIfA-|M~k7Bn3tPfBpOS z`}Y4|zknP@Sy`YB00G1VQttboLEtY##9yH3?>`J++5f+PL6rRiQNLgu1_potVgX{t z{eKx$|NOuHhk@zOpC3@OA=>```VZI207MK7009KDfg$woe}>=xgMa@Q`TZMV99$1f z=+7UZ=>P!)^22||KmS$!{Ac;a@cR$YT8L7JY6c`57{N{g2q3VN{s9Bw7X!l|paf9r z=TC&I7$7zP9Rm!dKY#u(FaQJ)NCWfVKmVWoW?%y` zm}dTA_#*kA1?Z>0QqoLZAnySq0pv->KTM2<4My5DUW>hHBSpkmCRUfavf4zkmMxWn=;={POe9 zpZ~vq|NHajABg<@_xGlmn*aic1*nOE;s2AbPk~B-;NSn>+$?|p{{I~>&N%tu*Drs5 zfBO0B*Z*HY)v`RYKsx~fhzW>+Cjb5S_s>6&>Le+qufKnBv&bF%YWVBlABO)w3uGC7 z{rLq%Ks`WL0R#{e(EPs)fB*akI^_5N|9=)g|N8#t9~WuY61}otf4Tqs`!(tD7m$X( zzkdG%X#fZyCZKPCW&?Et`7z=QS^~DI8Yx-=nhgK{7whGvYgWDg!B8&G1k~{7FHk81 zKmY(S0M7pd06qXVA~x>%?)v`v$?y7WENVL!I|c#<=Jn<;5-<%03=swpg4MkH|N9RH z59P$=0*D2u3CIB%01Ag+CC5rQSvY_G`T70NcNRvLcR${}{{9+hIZ*cRKadRo0R++j zv0Gk5I)>DQ+oULEUYSww-@{VeP#&z`*?j4i%tcfB<3v#u_k;CAlSmszIWE zes6rZ@!!9HKqG(r{Q)GE1e8GG4GeUkhF=T-0mR5q%E0gm7LmVTk@^EErhg$tKMw;y afB^u%K|axUkwLit0000IqP)p`2X)eQ1zd`|Kz0p8A$%R z`bTZnZKVTj3KfeK`2Ky*5B&e@&)?sFfZ#7s13&;VG5q=W=RZg_5SU54z5Mv&imk%` z|FW?#NF2HHZpKX|PKHOnfBpIO`|qDW|9~0*0*DFdzkh#$Hv9ux`j_+Xe->2}ZC)XM zHV0!dKPS0Ur}$Y}|Nr_6wBa96o`K;HKmdVl_zUt7(16dMgtTSeTz>rQ?Z?ko?mjsC zklD%d+V79-zd^=BZ2$-$7GT%`Z2%eY=kMmT9MVkp=Y%mFes6g9+4CZ|=Y~?>`*tz_ z9Sja7kOqJN0yzojravHW{k?SeQ|&okK5=fKkARLe6n%c?uEf?;0ubZ>f;9XA2p|@Q zU%!CHiwJW41sn7GB}e}J|9v$?qW1mmH(Bw2pdk4RbO%twKY##YWMEE2jz}>2 jgG53Y5Cqh~01#jRFwa04;J&RL00000NkvXXu0mjf4K^ZQ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/mq.png b/Mage.Client/src/main/resources/flags/mq.png new file mode 100644 index 0000000000000000000000000000000000000000..010143b3867f21e7791b8254e806b325c13b2895 GIT binary patch literal 655 zcmV;A0&x9_P)$g8u&g1qF5h0Q6aJN-{1Mbz`Ie0O|k$ z{sM@ZfzzR@(T$Ir&B098+eukMUeMl2+1^x^fq~&CAE%jZ{^O4ipMU-bgNM&P{`mQajg9%ivrm#DT>t+4XJGgP z5I`&pzyAFG^Do#{Ra%^vg_(hwneqG2-w+MozW@IH=Wj)-86P+6=Wo9l8G*Kb0|+3- z*{jc+=}JF+`H7W<>HmKQUT#*P1{)(8poaY?9|3*x^4(W97N$Rc|4EDS&R=&5Ab=SE z{{73y$Ox4F`Rgyx2o@H`|EK_{=f}^#%*+g|tSlctd;ka_mU(L~nd(ak@UpY9GCq0n z(2rN05Jg0{{a96b$W_S@ACL(Rxa}M z`G=gy|Nj54lTiQv|3YG^`}+MH7z}h_G^4TA+~V=RqI&{}iQ&&bpkF?J{dw&CQ*JI+ z5dlsHn4|vw{rBhJzZb8+p1k+fF;B_XE! z42<9q0s8Xa-+!garV_&3Uw`~!Vgv;%KmY+HvKbgYfiyF)F);jMVE6!x-Ip&{0DU4U p=>-f&1_my$6wo1$fFwYG0RX)13*@;vt7rfK002ovPDHLkV1lt{Hh%yB literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/mr.png b/Mage.Client/src/main/resources/flags/mr.png new file mode 100644 index 0000000000000000000000000000000000000000..319546b100864f32c26f29b54b87fe1aee73af21 GIT binary patch literal 569 zcmV-90>=G`P)rBb0vs`uywH-=91j zzd+y*NDt6q00G1Vbl+cwzkmM!`SbO+m+YTk|9<^u`R%^_m*bXSzgT|#{QE0P`4>nh zQ2ZZA13&;V0loO=-(MgHX#3gMzn3`u{`KzHucyC$Kl}CT<*&8wzt6n|ss8o%*Pp+C zfTjZk5DUl#p#T2<`NjV0^vyqym4EMy|26&E??3;3WoiC?{O#A4)4zWSfZPMI0U&@t z8bCe+8UPFj-d`E(e|zZv%GLeN@c(z$rC;+8f>rHtN0D&|BIS>OtI{AOC zx$tZGDUcCBkzc|7XAeyp!)#=hy@sHz%Z8Nmi!G71?uGb{rk^vkcEsO8W!D1Ad0T=%Vj%gtVfB*vk>3V2g53(}_00000NkvXX Hu0mjfpCtxQ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ms.png b/Mage.Client/src/main/resources/flags/ms.png new file mode 100644 index 0000000000000000000000000000000000000000..d4cbb433d8f9fe49f06585dc46ee15593e3e621c GIT binary patch literal 614 zcmV-s0-61ZP)w!6%f1G{My^>{rmp(`~Up={s8&+{Qm#<{R63-jRF8M0M7pe zp74|h+7SfEy9M#{{`U3;5)b(M0y+2j#`QMv`vWF8IQ;$o{QUp?{QmLR&;S7V0*Gk~ zkHv3!H5OU9Kb*Y(q-ELEH9vC;U*%N({+r?VuV26a{F4-VqagC^H&EN}zd$De1Q633 zj~FHafsLPJA96}RV-#A)X~v?X^MOAye@1K9u&05Jg0{{zeC zH7h(V@9+2F_Xpti2mAc}=j!&)<^S{h0sZ;_Q7ob~2($C-{v8Cq(%>lF*}4LV2^e-j zV}AbzqQAd@=ogUu2Vs2v^{02G<;{1(|2aQ(EV23Z2N=G800M{wXv^O}|A7VrH9+)$ z7=NG)mcRcxm)QV402KN07wDuv009Kl@CPU%A;t|Pff9c}I{*Fo2PXf3L>PYm|M&kd z6T{yxzy1Ri0|XEw14AS%MzNxQNDQDd27mwq0OfZ^Ej7^!+W-In07*qoM6N<$g6Gg9 AMF0Q* literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/mt.png b/Mage.Client/src/main/resources/flags/mt.png new file mode 100644 index 0000000000000000000000000000000000000000..00af94871de66cd0fbf0ca8e46dc436d66e2f713 GIT binary patch literal 420 zcmV;V0bBlwP)Io>l+&W2PtRx|M&0z2M_*#`0)SN z@BhDl{r~j~sG3n$7H9)N0I@K!uxMy#e*W?mp&AJO{R1fisrv=e1J=Os`!`SnKmf7) z1AD;K%nYO&458rnZ>;h=xCZfJy-Z2&4h30>ylo1|%C8 z!A=4QAQtS#|N9SD2R9xVP=6R000Ic%Boqz*|DhQF2dLpM!{5I^H2?v`f~*=Iag5B& z3^Fndyu3hzLFn&a2m+c75I`Us-n@D94+enNf~Z%o__l08i(?1?2rvLmwOi|Xk;8TX O0000wT1SlAdCl&Y)$gVg>5qW^#Y{{Qp$|KC6V{`~n5M8AIn z$$wxH$UeVm6F>m5>|$VWV&MJy|4IE<4N06-W9 zfN4+=|Nq*iiwn$Q2C&jbT&$=TD3oCSlHOGFpRx(;BY;>KBo9alsWSh04>T2o{{H<9 zq`@}){__Va_~#GM1}QP2gACgN0*LYNU!Z^U|NnpRpWzq7|3Cl#|Nj5~7gmyqN$LIj zQvdWzhA$B4g=Z%5I`U&fgJtk z_g|1x{{H;?U6oxK=&at?J**6@V8K5Oe}Dh~1#%?NbbtT?*#Hy-y7l+}Kff9N{$pgg z_WK6Ie}+7v0!uEdpa1{-Wc>4s0pu$NunhnK1op#kuysJ&8U8Z;`FH#0jm^LQ>Tv2E z__*)izhD3U{sq|pR0FmFAb=oF`osMFS8?ijP~d>v`s?4X6Tl#928Ghke_*>=zWoA* zI8ZS_0I~c5+3<%^Qj!Z4rNibE_6+kRtZU3J46>yw zfWS`r^B2emYGC;Nhw;yEHn21ZK^Q<1%m5k+3<-b$0%`dF7h*inus>j>Kov-;VIf zD*pWek$*v<0SxiK|NjEzfQkVEh>@X`f#DO--wX`DVCWAL;}4AS4@`0~00bBSj-y@M TF2~k{00000NkvXXu0mjf^ET$> literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/mx.png b/Mage.Client/src/main/resources/flags/mx.png new file mode 100644 index 0000000000000000000000000000000000000000..5bc58ab3e3552b74d990d28a0f500e9eb6209dfe GIT binary patch literal 574 zcmV-E0>S->P)LFc1LT4cKmY-iGBrkbGB7ay`}g

Nzrc?B z!}RAj&|v@p#KiFL&)@$*g@6D2{reke@BjagZ=QH_|HA5tt#{7v1Ul>guV4Ru|Ni&u z_rKqNfHnXG5DUYvUm)ZE{sF26>H%tC6A=5t#K$G4Eg-G_A87rbKVS#^0WyF#00a;d z1Ca3#WHt~1jRI=eclPL04k1AU{avRH{sTG*Y{UOQAO!R;KmY+XFaVtnvEdic(+vNe zOr8IIc?-17*~$Z`|L?Eg|G`fB4^+g!01!YxCouq>54Pd=FQ6a({TC7tjr5F#2GXxz z|3N{=01TJ^fBykB00a<7!|#88#Ce2)8ovGh$;ikEv=>5x7)(s8a literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/my.png b/Mage.Client/src/main/resources/flags/my.png new file mode 100644 index 0000000000000000000000000000000000000000..9034cbab2c02704b65fba6ecc4a7a1c1d053b6c5 GIT binary patch literal 571 zcmV-B0>u4^P)Z-xKO+5@Bcl`=5c~|G$6#Vc^TNX8-{JF#yj01a@w+002>mTdm0Dxpa1{C#)An)W@eTA0)PNwiNI_f1h_u_DTrb&+z}xv;{Wj zFGj0Cgf$G002ov JPDHLkV1gu%1+M@A literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/mz.png b/Mage.Client/src/main/resources/flags/mz.png new file mode 100644 index 0000000000000000000000000000000000000000..76405e063d43f2f3b5b9cae4f76d9f1c73cea25b GIT binary patch literal 584 zcmV-O0=NB%P)d!0?ZO;SY>NM!#Sr3j;s^4&s;y01$=&U~zyG1R{bN8&$-J2AZUB z{O~2s*67}BSLujJh!l{(TM_&V!SLx<0I@JIFetqFAHVYHe9OP56#xDPD){#oDEIpx z82$bI>))?me}4fPvfQ#j8vp_bq~ZU+|9p&pvv&RstuJJil>ZL~|9}umff)b({bONa zxh{1bAb?oDF)-ZV`m5;k=ljw>l6BpeezVN}{rlPPKR~N~|N8X{NCKH4>bI<%?516t z00M{!=o3jd#=5m)yzyNB8Nca#eH--U&Fe3(f#?+keR}os(@P+F`}Hl*IRF8~0t}ng zTmN>%etWlrgX7)b6MtD}{$cp_8-#v?7>pqD50GU3{f7;x0U&@Fe=;yIS7rUbaG!zu z&%cJ+j{>}Z{{3SByBg&1|Nj_3~Dr2e;Gh>zZrofD5`!lF#Z7{ z2B7#~hM#}`y~Cfd!0!j9&~4{}>qlfDw=_GI6Spo7yiaSL1(R34j0qF#yj00RRApetkm@ z3-<^I`04Bn_xS?+{Qmv@{`~y?GBy}bOB@O2-v*J4|MK+z`}_2=vH}QbDjOT??*y+^ z9PHma=D0k1DE9l$Z%$6;=mbqObq>D1hX4By|NHgp|L;GHwRPtz;{gH)sDXiz;U6Qz zZ)V2dKoY20MNK$ASA+NcQ;z)f|Igq2|MmO7w9@DL!aL%O?v>U60R+_W>(dw3rWqbw zY+vs`7YYhj_p}$}TRP+a`c40T|N0L!J1pWwjPJac>kgmX!|;RwAb@}x{`~&)@5xJE z5fO>bZXLnj?>WlS|KEEGbn$;~p|4%Vx0QJ+&gI^Ic|%b^5f~5v0R*z)@87>RPSSB9 z(mdPNGA>^D|K~4|Gn`ysXGCuKcCvq8=a*k!egj?m?>8{m00Ic8fvd1cUx@iTXM5HE z^H)FyGjo2eEq-FG+;YA1)Puvn7=8hj{`vO@7;-=x00M~R?)3!82NxK-n*V?K1yZeJ z{HiGaByR2;1J+usHUNMy48Q{FH!M8N)Pwp9Y)wTFo81A^+}-HTA|tp~ zO4-IyLWKVGUAVv#KrFzpU^vb8&*4vW%&*_Sfp-5+Oa#jR{(FIy5oqwQKVTa`BqPut z3=9AP#KLU;d%N^MnR|>Uj{MoN{TEQv&!4|iQhqnS=35v13ux)DKYxDz{qyJVA7#|2T literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ne.png b/Mage.Client/src/main/resources/flags/ne.png new file mode 100644 index 0000000000000000000000000000000000000000..d85f424f38da0678471ef4b3dc697675118bc7e0 GIT binary patch literal 537 zcmV+!0_OdRP)pTI(!tjlO;rIXl@1HV&)cpmDGW-GR z`3+L^2aNvy`v2$G|KGnEMPz|W00M~d>&=s#l8OvKbwEpiHvESG2>tK>|GyyO9|$ut zvpzk42q1u17-V!9I61+pfvQ08|BZwH@16Yr=jVTUlmA*S|6zcEk@1g`6+i&7`~&$E zYA%%V^7j8zTmS$3{hyirzos*?2B2bq00L=%yMh7Y5+V8jy0-s+|NJkm_8$o_f}Hjb zAb?n|yt|^zto-ZmuYW+t0)q+&<<KJ>BgrQT zRQ>1Q-#;J~AoS`6pHRCwBA zU~p!DquG0BzkdJv^XJbWKYo1u_VxSs@1MSW+P$@#L70Jomw|zm0SEvBhy{qNbE|>m zzkmO1v}^=;1(+C_zx@3C{m*wPUMbGM98Q`}5v}3B|NZ&>_xGmUO#lG|(!jv*|H=EO zfB*gc{`33)|Nj{n|Mza|{r>BFYe?(AfB!%K_3?Y%L>Z^l>!7{V2qIffI$#e zz5f4~?wt{_jNwA=k=tvV8m;04hxH1ClNsp|jaD0d_yUND;Sa;#|9^ps{`~n3bQJ?5 z!?D}Pp1*tk?A@~oJ0>vvX8@W7)C|%C)&LMdARGSt{R`y$`S<5P0|@TEy!-p_?>~S2 z1lj|1A`hhCuT1`^U1^@yK06yzfAV)~#?EnA(07*qoM6N<$f^cITh5!Hn literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ng.png b/Mage.Client/src/main/resources/flags/ng.png new file mode 100644 index 0000000000000000000000000000000000000000..3eea2e020756c41abf81f765659a864c174f89db GIT binary patch literal 482 zcmV<80UiE{P)E-@kt#fq(zr{dlL|rVbPb$^K$sU||3VAQpx%4ArjH z5b1ya|A`BUvomopF#HDspdkBi_LzW}KMcQr|NHa%@9#}BHvt3?3s4hK`pMU)fB*dj z0+88E|KR{Ac>Ck+U;lpn{`Xs!M;2%gKmf4-G0FZ zKmdWv|I6_A&;LJv{(%shYM_N64Szv401;3FKmf4-Jp?oxsQb@fum%PU8~*%)I0>i# zs2CuCKpOsn%?64Bg>X9QKhy?*00Lh)xZD#`v33$e!wuw~|G$AI0?h>~ z1_&UqlR!#;Ll9OQ{s1-n2ZahyF+c#Z0Amdp#**BUAaDHn%gD$G3dPzV3};X@Gl13bFaQJ? Y0MmSSAW;3b&Hw-a07*qoM6N<$g7#LQDb&XuwlP!fzJKM z`1`}(|Np-K;|B=>MS!;cWf13KY+LvSAb?md?)rCHn)&`aU^p;VI>1V00D#b-){yc34R8!1Sqb680000@|4`Xj5kLTv#?55wQzKoX38|NjLffBpXj6#2y{D-E;(Ab?mHL=FGT z$TP6>fK>na1GWK({(upf0nrSU5*Gz(00%w4e}91F-@gjtJbOE500a;V!?RECzy5mt@dro_q~YJM-#~SLe}OeX zNT7y4|NilCvNABd0|+1%hF>6clER!2lYo}|1|zVkK=SXOfB*mf{rC6ZKVbL)odgg- vjKFAPVE6?_e~=h|kVO7~qZmRm0R$KTYszPUy89K;00000NkvXXu0mjfB$dbi literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/no.png b/Mage.Client/src/main/resources/flags/no.png new file mode 100644 index 0000000000000000000000000000000000000000..160b6b5b79db15e623fa55e5774e5d160b933180 GIT binary patch literal 512 zcmV+b0{{JqP)O=a{vExMP2%`MCSoB^FIcLe_%lf;|~%E5I`(IQNh}3Ao>6Q|DFUXMn*>AqQd`w z|1kXd^B;tM|Njjl{{hM0zwd6?1Q0+hV1xeud-4=Wy?p-%sO`^#2S61Jzk!N?s)6X& zzhA%p|N6}=D+{y%Ab`Lc{sL9~1=0UN4*CD*7s%9KAf+JHKs~=eB-8KTKvw|-5R1&; zzd&a|ob(5%^Z$Q=wHy9p13+aOpFRNu5F>N&`Tk_-7w>=n{RejQzkfh&Kn{rf10?_b z{tFTZibx5v&dxav5I~H7|Ney-|DWN1$%1FyagzUW0464;_wU~W1Q5$TW@eGxtUvee z3vAf*8|igK9~@*rr66bh|NrkNM8z+V zAV?>O@ek;bKfu6d00KAO>U2WB)$@6+CBT{3ok>^7H@y{~6FRKmalQ{r8#S z4^Xx6Je&%q%J2@E@iKAb=Rv<(vO+e^|@#56B5{*~0Mg z>UitA{$OPI`~Tk`F#7!u z%=q{3H<BYX7n!%ts-g52=?&#%8AHvnDo=g*(t zU{@(f3Lcro01!Y-3_uV4{%5PNloM>zp5gQNALI8Qf4=|z|KsPsAHV+n`1$wy&wt;4 zfzU5-=mP{0FqD1)U3&iJ)0b~Q-+l7x$B&=CVSxxV`w!S3U{3%6(9;Y60R+_W0~jFx z85t(6Ir{$dcL`zMKcF!E2U7}WK$QOlX#fZyMg|5;)aZo7D4hMD0TloQ7ytwk}@P)zKX@%&%DST%gMqDF&nJ<9}^SM9e)7=i1G97Xij+!pc8>s z0}TKw06GH{9{>J;c@WiqflO8wrXNp!0t65X1H*raF>uuoM}q1_&UQ zzyE+PV}l7nR6|1@Xe}f}K_mmn5Fh~%KtLN9K+(a>4Du5*FzA2|{m;zE0Q4slD+}03 zKxJTefLQ+k0*D1@+|Oq}Kt_TL2e}yHXa<;L{(_vY&wh05Jg0 z{{*4}t^?F62l4F%@9_Wk_yrLV`1}4n_xiEzG57obO$p>A_3H5V5SZl@9UBk;0Q>>~ zF#yj00jE58tx;oDUfVAO7ZA^g)(IQ}Y8G-;ZB^s|2r!a55}@Ef&J{K=uDyPxdE3 z4FCZEF#yj011R$m=m{0X((d>7`QGgNy~*X%==~K4{m|+BO$p;F|LeE!5zX@o6aMwK z|0Cnry8?)1@m-dDW`RF^3@85Z{`vj)H~*iTuYX^D_V?HC|LG63e*OOQ`}ZH9+h%{4 zR^kLX2_S%&fc^l6#;?EsK&pTL{q^hL&)-1T|AVLlN&WsC!*d@f`}fa3pay^dVgYLS z^XKpHUw?lCNw6Z2x?f-t$o}(Zg7`(je_wy{{yg~C5U2qlfPfml0Rvc4oChck)CmlD zkWL^2sNoMV9{$c|wex0wa`2z{-@pGE82$nT5F-OaFayIEq`3SAj@n=l%a5DE9w9oI(Q6o;?Ey zAdrT?A3yTg*!;YE_t&FGSk*xQBO~LVKYsuMhzTgoEGG70+cq{OrGLMF<5CR)KqmnN z5F^kJDrIFqfoA{t^Z)mszkh!J|NR@N^WQHZ-f!>?ZqK&!y&{zFIzP0wn(b{R37FH0BS}Z;-(ZzkW0R z`o;JgO8$qifBy!003d+C8bA*C^$+NzU%!${e*gOW>-S%fvmgwhvw&=%Tb23#oNZ?S z2p~`>{R0^fayL*XP!U2MkOb-c1#;`ZzkmMzW%$Pc5I_(c{;*0)g6#S8SB49$^)FDx zACSQydx0wd{{073{+Hno13&;Vu4Mp{AoLGRCWbOF`~tJSfrb9TxDaUu27mwq0KDvZ UcsT?Vy#N3J07*qoM6N<$f`X&bC;$Ke literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/pa.png b/Mage.Client/src/main/resources/flags/pa.png new file mode 100644 index 0000000000000000000000000000000000000000..9b2ee9a780955566cc7dc2f59ce175f32d3731a0 GIT binary patch literal 519 zcmV+i0{H!jP)|lVPPN>g8sl5Km-s#EI@faK0YA%?%RKmGG2!N|AD;!zX}Teef|3X z_wWCI{=muCn>GOi5DUcMXP^GBIsf;|&;M~|Oj3M||NsC0d;h_|&!7JP`u*?sum8V( z0aY`}$^xwd2q2IKptK|}gRT_Am!FIf)j$AI2PS_*H2ec11_potVu8EfUYUvEKf`}G z`1lE^`X5*+Sm$r%-@k!200a<=Rv$}~=bu-f|Ns5_|IfewfBrH6Nrpdvs+^pde*a>f6qSrfAZn~Z?Fa+2||AuS*t4lqXH%-ra!^K00G3r z@ax~7e+)2nAoSxWko^y??$?YNza~t8coi7j009Kl08{<<|1U847wF+XP}P60U;n*# z@9*{N{$9Nb{(u|?WHSN*&{SqkP0pSkP;|=6L&63i zfEXEE-vV95@Z%rDuRkCLl>P7efnPxL{(%8d!yj-G00Mvj0{}mHT?%@XEt>!U002ov JPDHLkV1lK4=}rIu literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/pe.png b/Mage.Client/src/main/resources/flags/pe.png new file mode 100644 index 0000000000000000000000000000000000000000..62a04977fb2b29b96d01ffef3b88b6bf2ff05862 GIT binary patch literal 397 zcmV;80doF{P)@|A6@UbB2E)`X3x9D*gjYK@dOyv49j-R|ARv|Nb!om5Pi12buAo@&A8j1_q}8 z|NqCt{Qv#?|DQiV^2?@8009Kn!0`Y7lcx-S{{qo}h;n3b`}Y4|zksS4Wo3aj00a;V z*zEsM)xUqEs|Es~YOn@|-@kzx00M{wh#7&Zfj0j6!|?mhe@uX+0cadR0D+zK=P$&S z1T_5l12i2VfWS`r`v>f&U-&gJf}I2qKwu{UjsNuvF#Z0+ASnqp<}V}$85tQ7(W#^a2}2+PDh3E3#;-`R r2#VqJ=b;e{^dbb<+Crtk03g5s0zF}bJ8sS=00000NkvXXu0mjfKVzU% literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/pf.png b/Mage.Client/src/main/resources/flags/pf.png new file mode 100644 index 0000000000000000000000000000000000000000..771a0f652254b4e891fc73910aab38967864da54 GIT binary patch literal 498 zcmV3lobsI zohi_A`bB&J!~)dH$ngL7lczwX3_#UDxxarw>LBRX?|;94{rmL`$Yzn1{l&ll5I~Ht zU%nI;6$RPw9|VBf|1(_vbYnFmA3K-0+yDOt{~_StKSoBzkDopP1Q63dnCt(82%zd$ zpFb}6I_2!oo##J&nDhVtbEpQW0tSEpVuGrMivIupee<8UpWZS`Uj1hH_v_am-&g;K z1CTfW0R#{WvT7jw$rQ2Wy6P|4+kZd)xq2}(*g=aGrk)Yxu73al#Db(Bq?4D0-N;b? z5G()RfB&pB<@s4TkY)e;`2!F@EcYcO{->q=ymt>64xqsK^^5b@FIO-F$h{9?`~e2* zUv?FhqZcm%1P~}#|Nj2NBq_=8`#0mCzd$+0-@loGnqiRuWPl>)F9-k?0|XG`aR!E8 ou!#JF#Q1|6-w+1S#{dBa0Kx%7Vg$%BF8}}l07*qoM6N<$g2}Akn*aa+ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/pg.png b/Mage.Client/src/main/resources/flags/pg.png new file mode 100644 index 0000000000000000000000000000000000000000..10d6233496c10e52ead975c5a504459fad68ffb8 GIT binary patch literal 593 zcmV-X0Hv=@BjaQ|F{1AU+wpQ{$Kys{`!CTH>0fVKL!SX0Ad1a`1cQJ=-<7ptZmH9SAnX3 z{Q_yQ`~BbL*ME+m|K)!Dcl`DLKS&U$0U&@t8vg$M`{&P}#F!W*N5{%vzvln`{r&f^ z{||ospYrSf)?fc0{rZ3A*Z(w-2B2bq00L=XVPVP4%$zZO`i8aZ($dmav$E#>{B`Z@ zum6{R{h$5&|A}A!9e(}a_zUO_pbY>4IGm#s06-9cg3a4XeYH!D;D>_*`V5R3;NTtr z%*@^Mq=>M$LXVh`5jChqDeaf800L?F_U+sH_3M8E<^KOyQ&ao$p literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ph.png b/Mage.Client/src/main/resources/flags/ph.png new file mode 100644 index 0000000000000000000000000000000000000000..b89e15935d9daf25173f89a36d8111824fda5db5 GIT binary patch literal 538 zcmV+#0_FXQP)N~0_Y@w0Al+04@f;`V3_{@|8#9F=K6XQ9UY*azdwKd1Cc)=8bAo50U&@_7#Kb? zFg*PK|H1$NTnr5Nxw#FRn^&xvtbK0f`#-;cuKxpc)t_H~fDExeq6ZmH0|XFI1NZ;` zGXMXp{AUnj_^>}zDz4B$`$1}#7U0X2MOU=aELU!7sY=Kr&1{QvMCSsj$|2dLpM12Fg)7ytqY zsKMy}yX6c^FZ@4woIyZ63`(G)I*8l(j07*qoM6N<$f~Z~XumAu6 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/pk.png b/Mage.Client/src/main/resources/flags/pk.png new file mode 100644 index 0000000000000000000000000000000000000000..e9df70ca4d63a979e6bcea2399263c081ce5eaeb GIT binary patch literal 569 zcmV-90>=G`P)`{xe>ko^7oADI06=l8$gn`UeR2p|@S@&Et-|M&0Ts{jIsi2(+HrUEqpC9EW^)CAOaJl^^9-%lBC8BHP081)#ChQGgo zHUI<=3(zGH+B`^Tu)Z%{q{PpYS&%;j+KluCr=pK*_e;5D)h=t+bKO_wx|Lu6N(?HnJ zN6`lu;-7zgR^V0GaA(7wCwqWF2h;!%KrH_l5Kj8_?>EpHKuhXR);;uSzaAK9KY#yZW@T0p zQ2G1+FVF^n0AhUp{`o&J`0?k5YKbbt8;l4>j(9ExfB*vkhsr>Vq>*li00000NkvXX Hu0mjfu=^7c literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/pl.png b/Mage.Client/src/main/resources/flags/pl.png new file mode 100644 index 0000000000000000000000000000000000000000..d413d010b5b097c4e0a4604eba86dad79567ed16 GIT binary patch literal 374 zcmV-+0g3*JP)Ab?mv3iQ8Nu5A{|6C_|Nk>HFfjc`0biay0|+1% z2B@O{$c+Ek03##gzdwHf0*Hl3)etG5lK=vU1?W~rxN0!OqXDP`Ab?mH`1u+7`u;zA z_W#cxhChG)!_n{G|9=A+zrl>(AX#bYe+w4^1Q6Is4VEzI_51&? zUm!LT$@u9L&^Z7B1a=Zw2xL66B2?87%l|Mi00a<712fnG3~)u5+Wr7F{AKw27w8;- z00L=X`u&GNQW9*;Uxq&*os3{386YGO{rw9;K*az7#P}5)xp2gQ%0-g`0)PMm0MPYZ UsK>Njp#T5?07*qoM6N<$g4HXSwg3PC literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/pm.png b/Mage.Client/src/main/resources/flags/pm.png new file mode 100644 index 0000000000000000000000000000000000000000..ba91d2c7a0de26e554979f6351d42a1a4e22de3b GIT binary patch literal 689 zcmV;i0#5yjP)#D@XkmAv!xDMMW8-mEZ&mK%|%B2@pa*J{pUD?(_ahBqlG}*TVt; zF#yj00{;H~{{a8``T70x^5Ww5-{JLFN2dS#-{Io#@$TjE?&8zZ_V@JO>E`A8`2YX_ z`~rx{;oS`3$G61pUY_{!-0$E2{{CbX;QPSJ^3BEStfg-V`n;-q6+;t+Xyw zOy)sS^8$#8OF-b~@87@v{bTs||L?z_Zq~=&zGIaZd&b4_{m<{;|Ns5^`sL5xKflEU zUjXHSP67x3F#yj01nBVe84eEr00I90|NZ{}FelkBB-0BC^O2xB85HWz)f)HsDi#s! zMLEolpK<^Iq5=Rh0M7pb=>Px;h=2d#;^FNBZtPGJ5bKP1n*{>+?(qio_5k|){tF25 z`1Sjqtv%A&PR-7P0st`p&i?}b008M5=kFQg|M&R&{r=?U4x6PnhWa{`B$w z?CApk{rU?E^8f<&008;|hy@rKe}6M^a>=kt2+9et{QJXr>zQ5ua`W#$8JQS={rbi5 z^Ctr{)6XBj7@3%V17i>%fEb@MFnj<<;{gVSbOv{z8Q{qN1r~!u@ISB;HgNQF0R$KT X>6#2<48ze<00000NkvXXu0mjf$fQr4 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/pn.png b/Mage.Client/src/main/resources/flags/pn.png new file mode 100644 index 0000000000000000000000000000000000000000..aa9344f575bc92f4c1a5043e6e7d0a8b239daa64 GIT binary patch literal 657 zcmV;C0&e|@P)$VEDzr@CQtS7ytnPF#yj0 z0a0(~eP|Fd7x&oU`XvhO+V>M05dgm54eRCe)!y3s`vCp^_5Az)`uqj_{QRhrlmY-T z0M7pd#W4OILofm6?eh2PFCO>E*7Xbx0@lgN z0st`p&i?}F01o{F1pxvB0QdR->g@pe`}O_>?Enew{R9F2_ajBO3l7@{2K78Gz9u`z z008^~05Jg0{{!Vds4FN77!dx<`5YAi^1B#F4D8&$5I)oT72fOU?&Jm;1k~m5*82ws z?b$fe$;$!&F#yj01PbEP9OVUy$0!>9?*iHF&;9=9{2wXIjNS}D{P+g={1WdM4-6DF z>gCe?_5uI^`T_tk0M7per>)2uHb&;=(#87${QNQl(a`kw{Py}Y1nctu{`UwC>ihis z2n-DS{QU(1_5c9-0*DC|aR2`P{`;Tl?{DV6zkmJy$H?%Xjq&fFKfk|zXRCbma58?r#f z00G1Tv;`<3F2eQa&)+|P|NZ&>@AqGj(m#Ly0)zk56?XydpHmj-@bfY4J_0oTA3y*x rGBBhgM-@1KAF z|M~a-&+mVKfs8*8^zZj?AObQ@tXl^VK#U9w|7)um9zFT5!1JoGnu}M2{r|uJ|AC@F z;y(;9F@d=M{=L6-3m|})KsNsS&&$bh^PO0375Ag3EPt6;7?_!X=sz3*^#j#0Gyh{@ z0SF+Vlm3Z`F-Ar(?BDbM+y#c5od5B%>y$nn=KcNZ|L>nr$AC#T8JS}%RsaMLPy>UR z34^BAe{Qb-hmSD)`~82~jewZHH;sOtVgB{~2T_Lx z^M7~ue_6TzObGu%0nl7VM#g8?t^ouP$PYjMFazDG!uzbThlgL98zlelKgeJZ;~z*Z zSPxJG!?$k$0R+;(`ulr=6P)M00|Ni~>`!`VeZ#eh`fLE8Q){QmQQ=eGY$|NqCtU}^v=1iBd@fS3^8`SJ7r@8ADF ze*C{^(f_{w|JZZ@y#o+HED**2KY#i6{~yEeU;qEJ{Pzp|pOucqNnlF=0*DFdB%s0n ze*I+l{{11(um4^iSXF}z{SS2YKY#!N`GNV*AE2Jys=p_Au>JnK{@0e3zk%WO_xG>A zzkdJw^XDIk{pa6bWqzfjEk^+Y2&CaX+n+VzlIN6JC4s^J?+-}zKcEU=eEj+Q{|`_P zFrfeb|NHmfUxq&n00G3<$-qz^$iN`P!0-!({va{_z!?9)Bm)CLfB^vHpj0t%_B3$- O0000op82)aAGO;3n0AgYI#891D4N?IF|Ns4E{L7&Dm*MXp#y@|U|NLS4 z{rmr)KmTFmmra`h0*Hly38?u0llM=#nEx6H|J4!x{U`A6pVzdvv{Rd?JIrI9L zs`Fos+0`Q4a^C}Q0+k5A1KaQ$>Lf<6lK=vUh2aka(D+|}f9<^YM_B9kC$A?0Z|}3+ z`ptao7t>dSY6f5^0o~2O01!YR8-T_G^}PA>yZ7X8cHUpx=KlJ_{+sC?ST#h$AD{+c zSpNn30U&@_7``$5{_{tYU-I{#-$2g*Lz_YNHw#c5FVJd`!65YaF9ZRd1Q0-s48{x$ s?-&?Px# literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/pw.png b/Mage.Client/src/main/resources/flags/pw.png new file mode 100644 index 0000000000000000000000000000000000000000..6178b254a5dd2d91eeaa2a2adf124b6dba0af27f GIT binary patch literal 550 zcmV+>0@?kEP)~nkZ zGRMDC%7~XK0S0Tx8Wd7-QG59jKr9SQAk|O4{{Q=*0jSzY^rW5C9>)KFc0aM(`^fh9 z|Np=Kfe=u&EDs~YzkdJ$!~!z=-~YcrJO42FE6xsAYGq`2_Mbt_P-yG_e|4Mhx&31J z|LZSE!|%TgKvw|-5ED@OU$BE2n0|Z9O=M(v1mpqrFfl%gR9?uy{Oi{rkOq*RfB%6# z1PCA|pp${Bfd()#{N!P|4fj41<69Aq$E=K>;Tryc4F(7xpbZRv|AD;q^*8I6-->Vz zfB*5n`o{nM2Peo6f5F}W+3*h_fS7<7;^vxc*?8?`g7w0kCk@cHxaHDByoH$n$~K0fYN5BQR!wAqjL2Kmai^lz(OTgcO%Q oK%!9YKcr{|u^AZn7ytqc0H_5zuk@Q*SpWb407*qoM6N<$f;OS^T>t<8 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/py.png b/Mage.Client/src/main/resources/flags/py.png new file mode 100644 index 0000000000000000000000000000000000000000..cb8723c06408828ce68a932ff472daabecc64139 GIT binary patch literal 473 zcmV;~0Ve*5P)@|4`Xj5kLT%`al?B=W5I`&pU%&omV`KRD53KJ$2txq_ zg7fbmh|k3I@68*40Al&@kBy(1nSq%Zss!k?M~{APS@-YvZ#JJG201x|0gQ}qez5@r z5EI0wa6>`h%cteq@9Mue|Ns2{KQdro`19u4 z9*F+^0|+3-w;#WAv9bOIX#?r_2Xj8qX@CF21pWab0}C_Lljko10*H}8@M3d^)Z>@G zf!6*0!vH4#gGo5!|DS*V{{HzdC&9L6!fAj26p=v+03Zy*=I{f8|9_}7o838uP_pBd z=z9{Sra^iHOE|+IC{ z-`~G~{{ZRVfB*dX11A6d`CYbrEkFRVK#Yg0UeQ$d`q2ZhA|Uz&)bIy{egR1#X&Imb zfB<4bR{ihazwp{dkXn!mkfPsU#_wMs1hfID1|Wb~;Esc6TU=EJGWFLlunkbN!Q9_} zL0)432p}e)sSwo=4bd$f5a~a^fBpd(3|0LbOac`E1P}{Q!+)sq|NsB}pI=e{a>g%& z)4?|U{s;0mNCQ9sG5v!Z57O~Jp}QY!5N0Be0)lgG%);U`26kn-@hOg zU%!6+4+cOs(0HIde9xZz`}Onxub&LUB0x(30+2WcIRJn#2ut|?gWYu1Cf+!-K%B8# zdf?1WA}#uZ8oj7u>$I1i0Al&`=O0k%-@icgAIJnM0xA6maSq6BK-ECw|NZ*S`0Lj% z1_pot6puj;05Ax`F!=umqj7^frO?t|3^&I1kxUq9yECc+jQpY84SWH_0#pxl$?v~F z@*hy-KN0|X07U)z`4{NpU%#2aHUI<=%a31wK(7Du52Oc(|3O^?R1IN+RRjI-n*kVB z3=9AP#PZ|EACPLGJ%9cJNh|>9B%spYzZw7h1%?tp0I_@ndg9MNE>313@6R75NcceF zkr51-#U+7;F#`Sf7i0rK0I_`g_NQ&Zji7_t0d_@@ozArL?Su`s;*Q{AKpRQ~TD44PX+*q00taD0I@Lq1k(S2nt`tR{g)9*1H=ZfhCe_Je;NM%1^NLXfPkU&2dGq1gcanC zzsyWb(hLmW7#V^70~+_6f#nZ_(jSmgFakOWAb=Pd7`8Gn`~pjZT=;x%FbfOAzdzu3 l1(F{a{)I#17yR-T)##NTdjqb^wzQ(`1@?t)Ix4MUXz556teM9A7Ic zq_@itH|pv>q+zrjZJ^Hx5bj=fD{5McI3ol<@^-l_@~tZGV7p>1CU&qG~{YccyC-q z$8~P)6sG{nMmQy85K$E6L33rja$x-b9$ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/ru.png b/Mage.Client/src/main/resources/flags/ru.png new file mode 100644 index 0000000000000000000000000000000000000000..47da4214fd9edb383687c1d4f84fe8b42a51ceb2 GIT binary patch literal 420 zcmV;V0bBlwP)X|NRSO0LlM<{-BURBqYRGSojej zfLOL~|EH_V_~;P>Nc10*D0|Jxss< zFi1)Q<$$6LU}rIc*dU*QNFV}+9T))>0|XG`SD?F)5CbX~O$rDA0t^5@iDe$xIAIn5 O0000N_~0!B1ZtR02zJmAl3i>|Nr}+@!x-t zvcLa8?7#n*fB$3p|L;$z$Rx)9zm`4EX`B2HAb?mPX21FhQ~^@@2dMNf!=L~E|NQ+A zWdHd6KUK1c=hsc9f8Rnyezh^=0R#{e(Ek5GMIci_s{j0D`2GL?@4p}ozrlKzzsUIh zhw0@n(cNzYfer%*AeP_1{{8vS`0Fp&84w#lmi+$#)ARI~X!Y&+KyeWsW`<9n00M~d z^G^m==F0#79{mS71ZX;t`~_q%fY`tPf!Mzw;*3o4FJA8j2p|?NkT1UdXZrG=;TOYy zh@M|qNhT&CJ~jq`0Ac}pl#vl6#K7 zFA)9u_3syi0VMzY2QmHtx&Qtu@&7&0&HxaAAvs6^7=(cU7?W7&6Z-$piRe`dyDK`^ zN$WO$zWL=wEu!PO?Vu9@iVSM&8cWvf2p~p=WCn&G3_$lI&>tl77dYY}Tp)vm0U*Et X__=7oxWDB`00000NkvXXu0mjfV`BRN literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/sa.png b/Mage.Client/src/main/resources/flags/sa.png new file mode 100644 index 0000000000000000000000000000000000000000..b4641c7e8b0dd79aafaa73babdb525d3d2dc6a8e GIT binary patch literal 551 zcmV+?0@(eDP)4!1u&@QJ10&`rk^HbMk8Ee|uIe&H zS+;4$DbWCt7$DsBz5oJg0IGiW@flDZ69W^F_wDDmUw?kFvatRB^XvPsAO9KtfBpI8 z%a6}8Lb5=`00G1TbjJVx|9}4ZVWnuJEUfzZ$EVZx<$*voWzTGcp~$d(=+V{_?X+ z3y#kPS_RYq5I`UeU<=TsD9Bk~@o?qi&=DPd(?v86a{(`&&3I*E&g8|Wo?Sn;v*0j086Wc~d66UYT>00&^E24DBZ$$%)DV9SRUYEY{Wn z85tspN%!Z^Q&_X+!-^Gee*b1;`1hkUW~HJ2(d(c7|`w2)m##KhU^#|s1eh2YzMVW-Z(;)C{QJl7?-Rp6DTaSP8U8UK5I_L2Ffe?nu66~hU^Xzg zV`^IR|NmbGhChsd{xGln!xZ`Z|DQkqe?!SHn>GOi5KsdU{eSZ0I}_6%bMu4$|G#2j z_zP0e_xu0e-~WI8{{Q>e|6jj=B8;-KKpOx8hy`TyKZd`5nT(9?%F2RO|M~TQ@}K{E zz)C@?e?v4d{QeEp01!YxcLP=bXJGj0<#nFn|DXSVfvSK1-v%}nhwkOu|GvKejg0<-0ptJwK&1cy1oE`H<`Z?5M+~$7{9gq& zn~(3mv&(;P@Bi|05T(EW{(k!9>4UEid{_Ab1OPDr&i@1e0Q5gX)c>UW0I2)_6d3&w~i*%}NQ0G05Jg0{{#gAwk6j%|K36Wcrx-Z zDBuV5-R-~I;@8;l*yr}=_xShx{`~y_{Td7!{r>#`1^@zxvDKIVAJ?B1c2ZXvZZlkB uU;qZ}2Vg*eW0eWa_yywvNgf7(00RJ0?>)A@UfIF`0000~{{6QA+oUi>E@sC6|Npy9I|mRz%!e*p z2Rq5%zu9WWBC0CAb^-aHZc7E^YzcK`=8X^WVaOP zAZs{XYh)0l^y~V&U!Q;f1_jw4fB*tH>G!)IzutbUPS7xun>*B|^YmYFnaxh5oFnsv|ie?b{2U7L_4Fkgm jhERtq_4+^_K!5=N1|KO))1zs%00000NkvXXu0mjf8-+Z0 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/sd.png b/Mage.Client/src/main/resources/flags/sd.png new file mode 100644 index 0000000000000000000000000000000000000000..eaab69eb78776f8593b41c8fdc3fd65a86119a0a GIT binary patch literal 492 zcmV`tbb&P~tyW8d(D~GxMiUp8x`gh2bB=pZ|ZjUUI!&{yK1G;4d%$ zIuA;MS%3cg{`2Rrii*m`ix&X`h=t(`1IH^)u0vdsLXv;@{sQG7;18Jf2Mqqg05F6A i0*HaZmVqPy5MTgGElxU<64PS<0000a|fPuet^$h(7pHv_{jfB<3vn*8q{15ov!pR5qo|Ns4BVf^v!57RHO zo?l?}`yWs<(7C?=0*LYNK?X*pGKOD3v;Q#s|MUOfZxH$qCjb2cBQWFtZ$@_cPkRpn z1P}|u9|n-AAQdnT|9?TXAyk7H4FCQBl>h_~&`AvcLF)bhMS(&{8jwh^2qVzve;5D) zh>=0RIIAq+{+o}$pxEl(3%2T)`P!1-fBt~{@Pp~sA7J?W`}60ovKZgl)=2;X#KQ37 z&mWM7{{H^+4`$e}UqIEMAo>Rk|KALM|1kdh!vsX^3=B^I0*D0|DL|!?{M^6*VPKPH z00z{*-?BnHps)wJ`QKllIe-8D1EGJQNCgNWMh3}bctpOPvlbWzK!=K^+cJPc;};D5 g19O2S13-WQ0NlBGh$rR(5C8xG07*qoM6N<$f_QiWUjP6A literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/sg.png b/Mage.Client/src/main/resources/flags/sg.png new file mode 100644 index 0000000000000000000000000000000000000000..dd34d6121073fffcb2fcb5b9402b3e6361cded35 GIT binary patch literal 468 zcmV;_0W1EAP)dtOYis{Le*Dj#JuDs`KNc)tl9OYUmifJZ zA4uJ=-~WIA`uFP>kj*SB3$z3vfIu4l|NYA>FaP8D^M5;ba1<8<4f|GC2UPm+H&ER# zkRGsxe?Y{*01!YRC;dKnkj2`X)zkCW_8s5b+Wvn23^5y|0jv~A0#!5q{{4%A0U&@_ zfL8pya^=_l{r?#l8F+XY{;)G~as30ko(ZTFY%s&0Ka9WsFiJ`S6$1niy`;Hnv5 zfRT}r0R;fL{~@sn5I`&p|Ni}W_wFAQ`~gBVzyJ~jk&qMs5MThRyiZo6SsHx+0000< KMNUMnLSTY6dB!#X literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/sh.png b/Mage.Client/src/main/resources/flags/sh.png new file mode 100644 index 0000000000000000000000000000000000000000..4b1d2a29107be96413eb86e64a75ac7a3ba5793d GIT binary patch literal 645 zcmYL{Z%9*77>Cd9?woAXO+#E-F%m^1b0Xy*Qky9{B^@hJqB6}jOKKPc70u1CS|Ug( z7>SA^1`5-}4+VvY=3G*k2%(8O!OWTIn!4?td(P>GANugV4?p-l@2B^fCONIbD;IAODX_{rV|BCn_NC>%qlWoHrzH=l|0Y^Rhgkwr%>N3 z(d)FjlCqjgyY4&yRH!;rb)|Z-v~HjxIkvar`*JLyzxBc-B?Ix`3*qGz4q3JAd`#LY+Xw^k(ph!n`d2H7`aI`Eh(LrOLs%9g zj93;8ws%s88WHkIqXqnSf?YSjh=@dF-}4L7dS0HFB@iNj8OY*&4>%Dn8t&*i)aXz6 zSX_wQ?~e=9UcwhrAtAf8XLVoTbE5+<^|-KK=D&>)yX6u!zrPCrbEr|4Yi(XyIGTQI zFEDsraAY{)DhUd*DN;Q?!uSxvkoT|31dF#>2L0DGeRcNZNehm>xm~}-9q?gtV@Qz` zv-lB19|m}3LHcg92}TUOb+%v(0bnUhB(5rQI9?ZY)h~Hw=%2Au&~WB@t;^kVE@F0Y z%=8f1ZN}R1MniiNxkJ!a;3!XFerfimE2A;1XJChGXJ=)MAVRubE8WFo1T(1Cmhdfa ztzC{Qms6asjkstFkFp5L#maeek84Y+NtW^Wf=SRytjpC1=BCX4NH^VxnQ`+YXocAv zR?lKskkKZN7D>{S3>4;4+gPYYq0_5iq@jsB^}M0yMT0|p`lM;R_dwbVrBg^4RRbsq Y$WB%-43-yHbAJTXS^1gPjGK@C0`m$%7XSbN literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/si.png b/Mage.Client/src/main/resources/flags/si.png new file mode 100644 index 0000000000000000000000000000000000000000..bb1476ff5fe8e0d3af4fc6bd11e513d95fd9cccd GIT binary patch literal 510 zcmVm9@#;PEzrU@Gx(rNA|FHo^Mn<3|00G3vEO>ZAhtlWIf1*N!=PZ8p<;&lH z{~7-L`S<7dzu$lU0~x=8==VP$LqeQ==EOq)0mSn6{g=m2e$<)X`O9Zpu5&$Np~P9Kn=gZ0kf<1I-4a zUtk1c`~{K>fBu3(0U&^Y8d(1RW7^4V^6LZG89>!QBmXe{`pfY9FT=0Dj8O6)!|#7g zK*az7#L~(TO=a{vExMP2%`MCSoB^FIcLe_%lf;|~%E5I`(IQNh}3Ao>6Q|DFUXMn*>AqQd`w z|1kXd^B;tM|Njjl{{hM0zwd6?1Q0+hV1xeud-4=Wy?p-%sO`^#2S61Jzk!N?s)6X& zzhA%p|N6}=D+{y%Ab`Lc{sL9~1=0UN4*CD*7s%9KAf+JHKs~=eB-8KTKvw|-5R1&; zzd&a|ob(5%^Z$Q=wHy9p13+aOpFRNu5F>N&`Tk_-7w>=n{RejQzkfh&Kn{rf10?_b z{tFTZibx5v&dxav5I~H7|Ney-|DWN1$%1FyagzUW0464;_wU~W1Q5$TW@eGxtUvee z3vAf*8|igK9~@*rr66bh|NrkNM8z+V zAV?>O@ek;bKfu6d002F*|Nr~{=Pyu%qcHbB24G}l{PX7zKmY(S0M7peLS*D^UKK~y z+6(XH|9gD^r>6bY*OZ>+`2GX^{Qmv@{`~#_{QUm>{r(dW1b1xK0st`p&i@3&%J4-( z6Giy=|7vRh4-WrbUHx`??d0*<@CX3>{QLa=`~Cm?`~Ld;{u~zu0R89!i0SVi2B1U! z{r&fU#+3gbKmA|6l!@v8=U+^J{{8>`mjUR&KfnL~{sUtC1qTg400I5L26X!4i0my} zo;{uZo#CfPX{7DT1DwDAf(-fnkMZ|^ra%9I0mS$d6bftr0mQhUfkCOfiE zM%HqlKU{maemr>kAILSo!3gZ8-+$N@WcKe}3J^dnRtyZx@9+FOdynDWy$P0%j62V; zZC)?>`}hA}zy1SB2;@n0006p>>h67sJD!pI8@uK6+#GPW%ce@CEFZM znTD)%K!F4J_qpc@AQp(ZPk%rA2T{TB=kLG2fBygZ4O9eD_xsnsUl6u5yEM=SfB<5# z6|+@gR}o|u1R4pn+XF1YrQf2oylS zAbS3(sBkhcTmc9mV6grHN=r(zLW3F{=D&YK84x>x_WT70A}|U80*H};VI?9O(a}F7 e1_Utx1Q-B;QgQb4eH!Wj0000PEol7!5I{@}e;EG$`~Mq){{FvoV+so& z6Zek;JYV*``O13q$!wq?2!V|I&+z9TKmf4-@!$W9Km&lr1Ie!(IeUNcyZo#F`{&0p z?&{CqnEw9%50U)~((nf$fIu4l{AK07*qoM6N<$g2hARp#T5? literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/sn.png b/Mage.Client/src/main/resources/flags/sn.png new file mode 100644 index 0000000000000000000000000000000000000000..eabb71db4e8275a5bfb7b1b8f3a8374d50da95db GIT binary patch literal 532 zcmV+v0_**WP)6{xSen|M~Ox&!2yPfTn)={d@hr-#`EU4p90fz>J~+=oo+i0%`d3@9*!wzk#ZO z2WQUEDXOGe*gO;$t?-?1~^z5 zels!xNuY7SApXOk^arFAjDSu82p~p=WCoxfknArIdOZOgs+`19}IpFa%0{xSUi1L7diFBr+f01$veIX40Tgn<}np4MoMn3N`o$god9 zrkzn;+j{#q5F}xeh49xZuI$05IKi0v3Lq917Le*!UxBJYN`Vr8|1$jj50VD5fvSOw z-(ZF~46zx($8#~-kUKOh9u19TNY0I~dG z`1hBA5y<}g_y4cI3}({*g*gAq^Z#dL_#dnH|JmpNJMaAe2etv|3x;0|00G4G>kr86 zzYquf`+xk=e@^EAQat}9xc>97{@;He<|?3qzo3u+2q2a}APrzwf*tnl_y6c6m z5?7266~G|=0}2mdsDK;{bP_-SF*1~XW%vY(NDyK`u#uw~6h{mUJPZH<1^~BhckWKq{Jk|L6Yw|Nrm*vcLXk{sL#*k)f0!)jRF?KX6FHLLC_7zyJOQ#RJf}AnqSv4E_E0 z_d>^cfB<4-C}v>z1dGUDNRj#r9OZBkNc8hC00bBS^Nnc?6(4BA00000NkvXXu0mjf DM=x{`~^7feaw| z@9%GBrr%pd7ytr@3Fu&;D>$G0;d%9&ckUlP_FsJLznOpkI<);49~%fU|NOOg<1c>p zU%YI;<@gu?0tjRS1JJ?0e*b^_hvDg;|Nnpg`SWY#%HJzjff!!CzZWj}wP?w&U%!5b zMg95nhXEjfKpOu4{R2W=zZm2}8km@V1qJfIu3a z^ZvQT`ny}__wU`%Fad@Sg!~P3A(#OSI%WR9r+OFw0*K`e1H&7RKZ`vj#ee<&!}=E_ z2M*0Ye}N7Llfc;c2aF@2)L(`#3;+SdxR!w-xR`AxVx?>``QKoX4p z{r>$=Qj`^FFF*h>?mzuiS(feBuYdplGyMPm{~rVab^il$89+1;fd$yvm=2wM1rR`t zfB*hvWMl+7=|4yZ16bGpcem@l{$rO?srZisfXY67`UDU_ED+y9gdmKw*T0{<`x@v9 zlkZkl}Tegi}1FC>)y z`~`;fe+5al?K4jS1P}|@5C%z67NEr6KmWj?{{J^f1u*QH|Ne!BH7L;kfnp0FfEXE= rk23safJY>Z`~zeELt=n*00bBSq*!cC{}>3t00000NkvXXu0mjfg_GrH literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/sy.png b/Mage.Client/src/main/resources/flags/sy.png new file mode 100644 index 0000000000000000000000000000000000000000..f5ce30dcb79b443ebc1615fe4889cc26e2d762b1 GIT binary patch literal 422 zcmV;X0a^ZuP)@|4`Xj5kLT%`al?B=W5I`)SK7Qoo3V-qKd5l^# zgv$taFhBq?zJ2?apPwJU>mjm?jEr~g+yMw6Mj$IKE$!aDd%wZp7Z^YoAQH^_^XK=U zKYvwJR4!h;2oOM^VEz63kEEm|Pzoptl!JgjVAdZn_zMF-#Q*_BG6D!N0MDLEDh$KY Qwg3PC07*qoM6N<$f z|Nj36M1Mf!|33`>e*gaqWCO|HKwFtO#a&sZ0t5gt0M7pel4t!zdFAutyc%F2ByLSib7d z-@iY8{9s{WsjaVha_-{UlG^LGvEO)A^**!$6$1ni3ljqaFhGRYPy6$qf&JG{kKey{ z|N8ae_ixs}{}``bdDhkT?#VOp#VvoBm>H#|fo1~)5aY*A1~#Xrf1jTKgW~`H-wePI z{Ri~aZw6pM{r$!84-_k)u=@Ltg1)Cf2{xiF#iKZ95W~`fDE7~ z|1pDN1{?_>vH!pR*Zxoe2p~qo;+=0k{eVOy5dHe~@Bg1a48OqWACLsaJOfY!7)!uZ dz{3I%U;rfUVNTmRI(Yy9002ovPDHLkV1m_xKPvzL literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/tc.png b/Mage.Client/src/main/resources/flags/tc.png new file mode 100644 index 0000000000000000000000000000000000000000..8fc1156bec3389e54d3c5bb8339901773a881e68 GIT binary patch literal 624 zcmV-$0+0QPP)Mt z2Fbqp@bUfi_XiXZ_x%As_WHU2H1hfc9TM^w3-JC0{^0Bg`~MgK0Qv%mWs{6kqN+Cc z+b3Uc-enXLWmi^s_2~6APR{Qy8GilxQ)j#p({t5y7`^LQ4&lw9Z0Ra5^`SSP!?%<&3?fU!$2L%N9{QCa@0Qv%mW$D2m zA^~iFet$Uihac>@UpF6ozx?O#ufL4HfBpRUndtzp5U;>@#TUPR{|1KG4}bt-0)`aO z`@jDF|MM3p^6S^{zdwKf{q+mXVfa4fgq7?27ZxvH%|34Sm*EEk!#{ukVgYLS^A{)$ z3^fq>3urc&4I=;kz4MwgW4X`2zkh%J`Om=vauPrQ0X6*k{pY`=2p7%A_P)?P zUwuVdAkKe=-@h3}_C6p$AOxEwQo+UIeHT5%mg3lYGL;@HP(LjqG0$?6F}(Ht8A z0K*^*BsuTDFa|;zm9Mc;PRcq|KMMBO%8|{GkrU*a2x&r-3HS3`xnqW1|R~39RolBu`vAj19S<{HjoWK z4L}tj8-Qm20eTwfuYbROLyY(bwgDi3SQvf*HG+%>>H*pSauQI_zkmNAP67ugNCPlf z{{97O00g6G8FFbZ~XHln?_bqJeH^V)*lb;YTJ&0)m(r7ytqc06HRil3NF`RsaA107*qo IM6N<$g5!7R+W-In literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/tf.png b/Mage.Client/src/main/resources/flags/tf.png new file mode 100644 index 0000000000000000000000000000000000000000..80529a4361941e01d1def5d581bf2847cf99fef6 GIT binary patch literal 527 zcmV+q0`UEbP)KfiwinSYl|od6I(APxT+{=azr6>87_{|pZw{QvzM zh#vfW|M&NwU%!FG-`~IfN=xzq?EwhD!Wi2C0Dv&)|Bp?Zs+hPi0R-LQn75%cY-O8s zAPsw;Q!9X27=Hcx_y6DDzyCn0!4P6RP{Xg^fBpb%_yyDhbQs7f8-Qku33L4cS@IvK z8OZ(n2kOgTzyGb@ek0IRW9ouaN6$P0x*s5bfH4)06qlIj4>;1{Yyp4(0|4dQTo>gF RMrQy3002ovPDHLkV1fdR=hFZH literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/tg.png b/Mage.Client/src/main/resources/flags/tg.png new file mode 100644 index 0000000000000000000000000000000000000000..3aa00ad4dface0a9c23744ab451cec0443f187bf GIT binary patch literal 562 zcmV-20?qx2P)@|6mN}b_RwI3=Dr@=ogG+VE_mq7La^(HIVqv%*^=nCj$%1|9}7Uoj(8m`RC6+ zAo~61@1NiQe*Xrt|Nh=Ea}z)Sfi-Y&{FjhmFg0aZxaj}$=L~=U)(L)O`TP6-zu*7= z1Ib@N^zYwqCU)6YF9v`BVgi}{>lcH)J%gU!|J)p)>i@rg^Kt&>WCWsLoQyxgk29dnvBpN5vQb0*LYVQ3gi&tp7jnGyMMp z@*mKbe}4Uk_!#8f-w;8De}5R+mEP<>2M|CkH{LKvi2wQh{WmZi827ytqc0C8?ZF&p#S!~g&Q07*qoM6N<$g49|K A6951J literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/th.png b/Mage.Client/src/main/resources/flags/th.png new file mode 100644 index 0000000000000000000000000000000000000000..dd8ba91719ba641502bc7ffda16c25dc71b2066c GIT binary patch literal 452 zcmV;#0XzPQP)@|4`Xj5kLTn#2^O%5QJf({+!S7KyMr1NSdb0?wsyYS6cNVdko7wub89?$EL;)x)00Ic8;m6; z*ldXL$Yz5{poPB}7`^}m5DPc2nu@9r4=+1782$pC1a=$HjDKJ@i17#P6G=(#Q>V-T z0*LYF&!5c9%>VxV!xcCX0F(uW(%ZLh0Ro8SA3Hl!NeKhQjeq|#{05SL7=Ql;MhF4{ ujRQ&nZP~jQAb=RZqDCa1IEDa#00RK>mSUL#9?hx%0000(5Af|u+{y|g&!S9Ft%(8imf-XR*|Ns8{XO&`< ztN44b9;zJZ^?&~W0*LYB$B#TbJpUo){|D*(`>^x>w|gKB?9z<#wQ%E^n3(S0zYh>V zEO6uD3`W^f29Wmu3`mZF`w$?27#UO<(rVJ~y}I}N&+p%VfB*XP3y6U1Uw^?QNbuL6 zzrX+d{i`Cbal7Zfib@b53s27mx! mU=U|u;Adc9XCM*)1Q-C-v^a{iiydVE0000+lNu{Qmp=|FW6D0*D1<1Ovm9m%kYQ|7p*Yo3ikU z7~kiESLFZv{{Q#iA0zqwf4<*Xu-*CD8$MYncA&8U0mQ`ckKy0n|9}7f|MB&2MbDkQ z1j{KKBL!IA2(Z7F;(zkuBe#mo9TT;SzkoLU{s(jwKmai@{QdX$=kGt?etVi7(p9*e z(|o73K&5wiaQBkXEB9so{`tH8jIplb^d$ZPi5h|uLD4*o5RnOS|{Qd{@Cr~j!0D)}y0}8T#$FHit{lE#dc*g~k_utsL zxj&Wpto`+k?f2h5mv;mM4gK>M6!-uE1hnA~P_?8O+wWh(`!4wX{Cn2j=z=is`~Uxb zzx=@K_DcEUJ7IZAUZAu8{sn~+Kmai^Fsw$4O9qB73=GeJhA}V*Ffg!yW&gnt6F`6g Y0QOrYft$MNbN~PV07*qoM6N<$f;FEW z@b~ZkKS1)&|37~i{`~p>`}cn&^2?@800G1T)Nta$zvt>t<757@uK4@^{4bCmpb7~3 z1tfp{|Md&VW|Wov$G`v(Kp+kO{{26+>+jbuf7A2+upj>Wf7$Q%RxoV1$N0e}Fas1Q1BW)@@rKJa|xATBWA`D>(WW1JFHv zzy7cP0k+{MTm!^O009K@!>LoJmn~avX#S_9;5YZ-KmTX``u`YaHjD&?*dJiPF#rS* zNW;&cKP?=87yABYZ~OCq-%n(9P{tpi22h9t6$1niNQ1e-pD1xDrvBdy@BT2zfI@>2 zY$OAO1fsuxAqeOsfB<52Wnfst00RHO2+qJn5C9Nh04p0wT74^6IRF3v07*qoM6N<$ Ef~oTCkN^Mx literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/tm.png b/Mage.Client/src/main/resources/flags/tm.png new file mode 100644 index 0000000000000000000000000000000000000000..828020ecd0f6fc73348373c9e7a235fdced09de7 GIT binary patch literal 593 zcmV-X0VKiqu&g@GZ_kb&V31Gky(*8`UselaloV1SZ886W^4001!n&i@1e004Y* zk0R&!|NaLC5DIk$@CyeC@AU9ebDYiJ)zswL{Qdm<{`>p<{rmm%TMiuO{1voVt6d+$)WVk%^O){nwx0zkdJv^%rQ#Z=eQ%0Ac}Z`19}Y zkI%oqe*ekA`2WvehFzDBOo*xZ{rB(cQ`_&qdGhP`Z=jyvAOx}jAb?nae){+CFDvWc zZ(siLa{c@D=g;LQw^HX+sY@u`d-M3!$G1SGAWQ!Ifj9{ufLMTG0<_`#=U?|Ye)D|< z8pH7G?=PVA*@stt1I-4xpA0e#40jk9 f${7d%fB*vk3(!F(w2U8u00000NkvXXu0mjf+94t7 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/tn.png b/Mage.Client/src/main/resources/flags/tn.png new file mode 100644 index 0000000000000000000000000000000000000000..183cdd3dc98c6957bde83f375a431e543a3ce9e4 GIT binary patch literal 495 zcmV@|4`Xj5kLT%gT|L_r@28Q3iff@h;hzaCwplYy<4FCT9KXmB7 zhQ|Nt)Bg(#gN*m^{Qvt8$Of<_K(zn?!~}BDpT8hi0uj&>pxItt|NHv>A2{&e(C|Mm zFGw7s;m;qS=>P!)((wQ9AF!W(0sZ}7M&|#9_5TYB{zpfHZ20g2t^wjCfB*tH2_nSs z8?4R7=Kq6S{f9wP5^T(0hI#XVIvK%6GC)WrB}fdh6SO{nUiXv3iAlW}aH!uJM5J&?vILH{_iZHeP0c!XQ3PuJ7 zfB*t%VEX-sK~fTI%wL8-Af1e0BN-qh5dHlNK|sX-0mS$fH6j^sMDRaYQUC}r0RA0L U-~OCsp8x;=07*qoM6N<$f)6&Rz5oCK literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/tr.png b/Mage.Client/src/main/resources/flags/tr.png new file mode 100644 index 0000000000000000000000000000000000000000..be32f77e9910c0896c1ee8e7ed4f0edf815a517e GIT binary patch literal 492 zcmV@|4`Xj5kLT%yy% zCxE~j7#aURc>+`lQtjr(@bBOM9XtMi`UFz<>-YcPzyAOF1!ObI$^vZw2q2J#|6qeb zs-vSBOie-Rl$HN?bb!_U0_g#3VEFwTr~x2=m_Y6Zss`GqsL0^t^nd#F|4B*zH*EO- z_3QuT%m4H6g6)A?0#pkSKujPf{rCwIb#eU5`( z{s2t}2q2J#|9}60{Pg58FvR|AYlEDjruP5RC6KEgJ^H_IA4CJhNdN)F0>q49OBog~ z{?Etvzo-c07$As@{Qu?4|3!FoS~(Xu$ujuK&Ki|5;f$7pYF;Nfxh(xqFs zZUMDjzjMbfEbRY>5C0Dx`v3aP|JSeozj_5kj66I)?%V+g05Jg0{{w&k0RJ5w0P^!X zSy|WJ-R9=z(aMV97z{4myy=pj4$^bx3|zy{hYtY)h`Ao9!QA5iyLbPuTmkB_PfP?Vee~$j z!-o&gUAyj)n+wu&>J&%=KmU)%j{yP*q=CWA45Z=rFNTYkfO?!$Q_o+&{s0WlT)E;? zRQUhj|NkdWF!J+%fBF<4fS7B68jOwqzkLe|6QI@S&;S4b-#s()-1Y1C!Qjl5tA6F> zpisYc>&x@!00G2Y!@$5~Xb92(47=Z8{{bD%z~Ggi4>bPny?b}>+&OjWQc!g@0~6Et z-Maw-h`Ac5K~JB7o9jOp7XvpB0}lu>-nqlT$mpA&|LpDCZ{NOs`t<42?c44-IbZwx z0Rjj;B7x{N1H(%OhUW|nPgz+XA3Vr_1a9x%4G>@ep<^{rq?UTJ00000NkvXXu0mjf Doa;3l literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/tv.png b/Mage.Client/src/main/resources/flags/tv.png new file mode 100644 index 0000000000000000000000000000000000000000..28274c5fb40e5d3bacd7c05d9a1b8017eeaffa6c GIT binary patch literal 536 zcmV+z0_XjSP)mzpv3Mmw05Sgm{~xFtLc$sUu>nR##!sI<0R#{WMEdpj-yJ7j ze)|0V&%b{_!~X#7`1>EofS_N0|E)GoUH&SsFy}2m05O4F@}ffYb$qvjbfSUipMQUU zgY^9U_4m&&5D8KZ)HC5u^uad<48Q&W1P~L@hOIAb7eDp_T4W)^*Xg48{r4ZBJ-_}k z{QCPJq~Q-pPnz693xQJ%zkUG(5aZwHtqd&u|9`*u&+TyOvq{_Ghd=&6oB?#w{~Yv(xYPgr|NRH*haKioowt*& z{dC`3v>70P7=Ql!$;`|Q@(GT>fdHT^FqGcDeG3pkjNS|k>E&sEfzAeo{ckY&=g(gd z`CCrwmxA8!XLtTy-uM6CzrVnM%iVPzAb=R%ks=j>et^j`MFs;Wh8c4i)_^1XKP)u> a1Q-A&>r+tW$-Nl>0000<~s!0-E7R}a|Ns2?4|9|}gs%Dgx1=;`*KrBFO{{8#+``2Fv2B6@-@8AFY{`(K80jdYk13&NH4;SYnOGgJvc0I@K9VW@Vk1}Xmk4~YK$|NH0P z-@pI<0D+Rop8&DnA}oIvEc_iD{O9-Y-=|4+U?1uFgf@9*z_zyJRJ z%fS3QR^zuR^Y0tyerNXndi(YlP_?gzto2+5fB<3vIsj-gQ0X6_*?&Q*d^>boB3^zoi8Kq^tbl{qlR!{9jwQ|Nij< z=(=A?F~8j-MSe1{Hl8Yc@zaXo%Ljk}VgUvb(2HJbe?0kqy?_3vsPXrUmq2rWE6M!I zE@4*I;Mw}vw&7&iFGdC>zW)rr{s9CK3xfnhrsi*1wqMhx{@J=6sPgxpU%z6)f4fBr zzxpd#a3tyYE8}0lKnA+|-+zWbe*glA<)R0}-=}{Pnt#7~1q_Mb;=;f3i~oshNgsS> zIr~z~(_bRL|NZ|1LO_>;A_pLVSoBpHQd9nLa7ao@{lMMx#__lBLe9Cjx?EiU z71{s)`Oon8FXP|;z>s7BItd_v7-5mj(Adb}701wVj$!S*>kNN{fN~5FV)t0eYh-DxnQ(+0I@JIfmA>F`X8wD?>`2h#NWRRzyE`zA&Pzh5tt#% z!^rUCA3y+sZ1@LM1hf`pHc0ia|NsB|1uKPX0CRu;1-SztfLMSo`wOxir1}rsY$VlC z667idh7Sw?0mQ-}a!Q0#;n&}vAb0=!_Zy@WNd5!6=O2ju7s{65Vq=gx0uVrqe;+b1 z$mIY3|KLBvF9x7j{{!v#g_UGtQhI;<6hHv607H@yECdWlR7EJN!LomVp$!l~APvkQ z4p0cL2#YobhQA>6hXEjfKpL3-19eKmL_s!=;22W8vpXP)6RGKQPvx1q)UK1Q6rb{|u}Q zRsa7#{m<~1f#L7}|GzNK{&HxZVEKJ4>jAefr9{u_M=MTegAo-i2ynN}MJ5kZm z42KRel$J95{>|{~*Z<$Y8RdVo9Ap3pAeIsa2H!t_KmPs=bot-kKnMR0GBy2s>sC=w zQFmwO;@`jj{`v(%zkl=p`BTQg01!Yd{Xp;j{qx|@pI^U!0X_En_iwRd$6{h)tX8f( zaOhC?&!0e%U%!3phy1#1%V=eK#YHXK~n$!{#(ib5I~Gyfqpu4^P)1J@ZrOUii!$=0Ad7ci1(2A^!3kwFaT->5i7Ttz54h6XL$JkA3s3o z=g)t?e>01U3h&zo5I`(I-#Hr#t~u}$WXZu-Kyv1Rm&rE5wcB1YFI@@=0EYkn8G$4- z^Xb{M0Ro8S57@^*QJ}#<$Im$M^4iKw z00a=@`Sa(sw6y;I{R{T)e~6KA2AmBHF`#D(ii!XNh(#nN}rF)(}plRy9vU;su%e@J`J?dSji002ov JPDHLkV1n4Y8}|SJ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/us.png b/Mage.Client/src/main/resources/flags/us.png new file mode 100644 index 0000000000000000000000000000000000000000..10f451fe85c41c6c9a06d543a57114ae2f87ecc1 GIT binary patch literal 609 zcmV-n0-pVeP){qW|y?pud`Sa)3|NY&vWd%S0u>b>P!2!lUe;6EF*#G_c zFVXVt@6Q{uX@40W{p0iY2Aa+A^Cu7i8KT+YH}2j52q4BskM2rJ$^k9;2Xxc_|Np=M z&VaLlA*IO5FlECMfB<5VUNC{tBZO(|zW*;@GJN;|bTJ71`0*d;`d`2P!x=ymOA`2> z+y@9C##^^8%gd{MW@Y91_2d742B2~OQNf=-zkmD?Vqkdk_wPTUNeuu2#KPTG{_;O4 v7C%8E5*DLB7#Kb?Fnj}}-(W6879hX?8lYRg`Y`<~00000NkvXXu0mjfD6Jtx literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/uy.png b/Mage.Client/src/main/resources/flags/uy.png new file mode 100644 index 0000000000000000000000000000000000000000..31d948a067fe02d067a8c2e69f28cca446bc7c57 GIT binary patch literal 532 zcmV+v0_**WP)_vmzq~N}&z08z z0*LYY{pZr+B0$d}2MCnI@DC;m3N;oM#uMkR0R#{ugY)L9Y<*xj0QCR^`!^)W!R$Za z5CobHbl5+T3;%B|S`QFFjQ1Zt|MTw;G#Vi+hCg5i(ELAtfD|ak8UBG;ObiSF0R{lf Wla#5zB1?M!0000JMe1P}`Y1HZufM;3|NZsr4^XwNEI-g5fB<4iWtjhPj`qjL zFGT_P{{Q>;-(R370Ro7T!5$Ws$Po%5A+Zb!3j_cNFaSC{Z(fWD@s$7o002ovPDHLk FV1jsy^u+)G literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/va.png b/Mage.Client/src/main/resources/flags/va.png new file mode 100644 index 0000000000000000000000000000000000000000..b31eaf225d6fd770e0557c2baf8747c91ce88983 GIT binary patch literal 553 zcmV+^0@nSBP)|05Jg_ z#sD@3Z1&&(|Ni{{_Zx)%v%Gr!NnLzkb;C z@#QWhJ{xI9$+*)bNX$S?E90e{K^EMkYok78VXhR-hjM0*K`g z$j^Tmm_>oEVgxdn{xJOg$-v0L$jHRN$OsH@P9Y&+^ne@=1^@xXcy~X;zaPM$WdOPj ri2i^{AeYDB@IM9-1r;P))4B5XW#s0WPsWLbObPT z00IbN14#96puqp%TKvBf)PA*{`~CO-uM~~n%6z|m{re4Z55!3T0R+GV5`UO(`=kK4te?c|?1P~}#f$jmC10=!0@{8gBFK@Zu z-~WJALqveC|MTzfA7GIDVE_mqkOrW8BqZ2?K4D^#WMJTBVE7%b47BbKBg1c?EHl#| zW&XdQF#ikE01!Zo3=CVrargu%_x5NZPzVyc@Ms2-@*Gr#1lu5DU{kh6%sopXB}uDxAo`@Bt_a*7F+) z{Q{%kjEu5C8vp``Wse8Lf5q7h438n|z~nEOsZga5l7ZnjPy;{!05Jg0{{a91!FF~d z;^O)I`~dv>{`>s@o}L{E2?GEB|Mm6!N=n`P`~Ld+{`>p?A0G$7!M*~B3FM_eAU;q6 zNCH*w+4I@c^Ny?Q?QPpWZQiU1R1HBuWkA0H1P}`l|NYDG=gP=Ihyj(0CItil0R{kn^jdV2 S*Eqib00003_-&c2+@O*Bba;fM%rBo$$qwJnuekcf#0k*RG7MM`2+5Y7dK4k?R% z*@ue~6f;u_A~T0evP6qS=VxXnGS_mt>CSmS&gmG`kLSL)e_Vf_=c&!fKB7`4C;*C(7s2O*P zOs1$U0urzb3<=a`d9ABG4Q$eK<6~5Cg~V%B8`UqrfRSN8f&?QTVICs^VT=&p*?Eut zt4Ur&-BL_ON(Z|Xfgo93lf|gRkT$Mz-7^OQoD^XMF@}g>R?uoU0094KbXv)l?aF0E zh@GXQVr04m@05R(Rjxq*p|CCIRfv;QJmH1kf!hsqX*sha?_l;f%e5i5I~4YG#H4;6sPG2&U~bv( zNr`eMXGIX9?wP^zPQP(6+&$05Jg0{{;N}{t6CR|M}we`P%>f+X)5&j)Fc04NUj< zI`HZ2Woe@k6UF22`V|Qe2MTi(6bb?WF#yj00*Hv<{QLmnFd+P z9@fS%;@9^E|H1b5`Vk8^2@Yw;#}(}C_yUN9iGkt%y>Cxm>T2lO13mifZBc!n`jhHL z!MK>$6Z_d%|GfHMapIVnqod%T-`{|q1_&S~IR%b4Z+;63vi@iI?>jBz=eJjkjQ{>J z{QUQa@gMhBW_GSe5B=V~`@+h~@c%z3lmG&V@$K8U{QUg?|NjRX|KX9x@@rf{MxIY^ z?fG>3gQ&3ruaE-Yw>fW_PfAH~0~Or8dmA8tSRg?T(ZDLQOiOw%6RWM9%$i>kkAMHu z-oH0qLBYb*loP0ck&zJ?LI42-(g0BnBsn<*tTlsx%zxbLe*S!R<%*E3>|Z7(9Y#iG zpdch>0Ro8e`Sa&!arp;m4%k`<1H=Xjf<=G;Aix0Vb{`Xo7A9K&0000@|4`Xj5kLT%B_22*h{r-RE_y1qN|Ns8=|JN^|IHRmA&<212VgZ}|A4N4# z+WpUe-rxU^{Q|504bi~x`!`SnKmf4-F(Xhl(8fQ1fG+vp^85d{-~aW0|NsB{f9tRR zg1`PJA~XO^2M8drlm7gLxRT-bpa1p0|1bLmQVm3Azy5pw{{IQ;B%q-{(*XjA3FM@| zf53hMJK)W)|CK-uK=ku}!>|93eu7kk5yVLV0R(o^Z;+)RSAk6exg_KF|98LtFaPy_ z#c#OrKtum9FaQJ)NCPuC$bbfbRI>n`@$3IHusZAC|2cmD|MKhqAD{+Mr~sV<5I`Ue zOuzpyNJ@f@0s0=KlTj9AB*>SXe;D}wK*A7+fQkVEi190GMB<5K2mlB$03I1qT8uIj Q5C8xG07*qoM6N<$f}XY6qW}N^ literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/vu.png b/Mage.Client/src/main/resources/flags/vu.png new file mode 100644 index 0000000000000000000000000000000000000000..b3397bc63d718b344e604266259134e653925c9d GIT binary patch literal 604 zcmV-i0;BzjP)7(YpZ_rO%ce~L0mQ<@*ghnz= zZvXM=_iyVze?clB=ogUu_5ar|Ae&KE_8$WSKmaiTHT?Vg|MaE5OE>+VAUAUr;?j4FCZIvLVX%uQ>mocOU*NTK^}%njy93_oP3+fBpUa=kKpye|`ZO zzyE?r`Mc+5Xcyo_v6>E-#|Nl{r(LkAS94bRQz)6 z1Oq?-fi&zp{`>yDUnV+0)eJF{zWfaN_0t9DjNiZhfxY|}ME?2z=O_CghC>Vh0R+AWw znBTkss=gWN$ME*Y`(NAt{QjkN;J}02MB(&;Kdc-80mKB<@b~YZKMV}g5wYSaSzrGC z<#lj=zkTD+H$ND}dANT6R9Lq3!_gzJFJEF25d}I4Ab>y`n3$OVE?Nk3?w(!me*OA# z|pNg&$Q z5()=&no#y6=Krof_F9Achf)A{nF9v;`|P8;$Q9J(&$2e@kA>cu?oN0tdr?p!r`I00M}SAs-l}3=F@( m=nn?tAB_7COfoP41Q-C;+8YpPdg;0V0000`!~0W%8&aG{{H+4 zRPp!M??1nPG5q=k)Wabw3seITKp+kO{xLwc{r>&$?_UrCGJgGL;^k4Qujka#`tk7L zjnovNAkb9+0R(gvNCCu>f4~1QF);xde}DY=^Xr#I=|`5hhp|NZyp>({@(egUPqbabS$v+tIbNu;L>xVSv%=>ggR z5I`*V_U`A@(fj`3!N1?XfHr^}17rYw{OcFcC69Z0BvMmfELgy8V88%&5l9HrLN(5b9xs8l&q^Gm-@&XkD1Q1BW|346ee?wJ+H2_`p`^l3V z+1Wtu|3AMO{`_T8Q2{Cj2q2bUARGR$NlJo(=nv2bFrDDw2U!bb{QV0-KNuJQ0*G-b s1H(6@xcrC2{sj~H2V-zBFaQJ?0G2^Lae{Q+uK)l507*qoM6N<$f;6K8u>b%7 literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/world.png b/Mage.Client/src/main/resources/flags/world.png new file mode 100644 index 0000000000000000000000000000000000000000..bb27b2c1cc761f28f4ca5daee7e25659b213701f GIT binary patch literal 625 zcmV-%0*?KOP)<| zSxyI5Ql=6_NeEqp8O17^7$seI8C|u4Zo2X=>ZXgXx)F?pO6jQV!pe%omLpr{Y~<#e z+uU~4?VO#RKVNIGFgg!>@O~dZempN9MF_#4IJ>=|Ys|2;w;|Vc@QkzBc2+5huC+uV zRwoG)ql1T@wb>2GOFV2`6RKgVgQlvmn%LUw14o99Ep~S>v4yClLZwQ$cC7KL!^*rQ zAeSj1i*2E+gJ9vP2i^-K13j(oct*t2P!|4B8Yi1}pq??{$!w(WOLW7hs2sgr@VCLy zu6?I|?b&_$&hupiRyM&&5;`~oyvO&#ZZRU9%?eAA7%) zhS_Mqjot&e)Y%BKDS=Tngnz78m>gd~DkF^R3YdcEe>4(&H8d5f+^0W9VUQIV%WG+T zvGO+l;`@v!Go!i!LTMTs$zJ^Mc4caIDUSKYG%O_rT=^`1P2%z7{A>SA#A7wrsJh<> zih_xubD?)X`)>E69JA<-5ee*C|6>(%ey48MQ<`3*E1sOaY}WDP*a00a=wNxxYB{9yb2 zOU?HFZD2h;3gJl2w`NO~f5I`Ue%-|qnfGfh(_6MlpFT>xzKtBKk z5J&^l?>`KZl3-*0GW-GQWCR<@03m_s?_UT4Dh3E3#;>Rm$$$~H$WlN65MTgr(_Ikn S3@&c~00004%P)h=Z!r1m*T3JtfNUmdS)dI70mLHE z+xy?%p5eHez0mS(A>sL-rPOy{yGk_s3 zz{tq>=+Ps90Al&~?;k`pNCN|I|RMxbJV00L=%3E(v!XamT}{{RArWx|9$7Z{pWi2U{IKN}lRCnF;x&=P&!(V3Rv&T;|GP6K500a;d#1A^U zy8js%fB*jT=jRXRU*A|5K#2L*r(ZvQ{P_O!=da%umKG4l0|XG%NkAKznV4BvSbj4y zawHV~fBoj~uiyXJSQwZYIoR0QIXM3O{tc7`Dgy{0# literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/za.png b/Mage.Client/src/main/resources/flags/za.png new file mode 100644 index 0000000000000000000000000000000000000000..57c58e2119f402072640ca758657798b621f3fb1 GIT binary patch literal 642 zcmV-|0)737P)Cfuwe;F7c=r;qyPX>nX3=Cfx7(O#Fd}LsF&%p4OfdL?Z7#j~W z{9>KL@b8bfkaUokt?1q(EJu$q{Qdp^&mV?AfBydlG2rBvO`8A$h{fmzgIW0J+B07s zK74uV#pQB`MAdI!SdN|k|KJ`--LK#OfB*Xb>lcvCC@cGqfdL=@05Jg0{{a910Lj6A zAt&mC*9zzO1pWQ@b1`Tn1`iV6=KuBe|Nj2}{{8>`{{Q~|0SOB6tE&PCq@k>=Oh@B| zgDu0v2b@p;u)h2C^Y4EK4rY$O|Ni{`3qc?^$?%A8m^2X}fLMSU{`~p7aqHLD@0rW| zzIWc|c>Ry-$DePXKfn0(_xG>AzksTL{re5n#{Tyw!wUw000L?F`}dEV6O*(3zu6Di z9{gne@#pKG|NpqSh1vf81DXBz&mW+Me}8zmL>PD&00Ic4!QY)xLzl7RCikV!EWdtz zoorLj&BXoYF88-DfB*gk`{Eza7yo#;S!C4G00M|*elvr*8B6X-zBgb0FtGkEHc8mM zoOScs_b*;Q0~!ksq<=swfJjA!^Ww!_00G4E&x@hvA~zQkvxU59n3VsHd7Nj?ec|R~ zkmuqAx#JHo0{#K*`TG}00$uYDAb=PdGJ(Ek5Vx?d6PS=4{E7kSFNS|$^b5xL14ayB ck_{lh0N_F{UmK66LjV8(07*qoM6N<$f>aVd=Kufz literal 0 HcmV?d00001 diff --git a/Mage.Client/src/main/resources/flags/zm.png b/Mage.Client/src/main/resources/flags/zm.png new file mode 100644 index 0000000000000000000000000000000000000000..c25b07beef894408ae11c3be294d6e0eeb28c0bb GIT binary patch literal 500 zcmVLz(k0+Rp#|Nr~{@4x^5fB>iy$Oe)U zA3pkg`SAPSuU~)vaR2(F*|!oPfLI`Azxw(Lr25~#KmY#x{rl(7|GyyW-|wp|{||Bf z=)Cuc_0O*fYWnY`7ytr@1!(pEfB(To0uBE22c#NA{{8#+_rKq)zyAFG`zKdY6euFW z2yzlY0I`6SGJqWbGV%`;{r&ytA5hcp-u`P`AAdOg`t|1D?}h2=^+y>10tjRS&?yMj zfBygt2HEouXg1LCzyJLD1w#LR1MLR}1V8|>0KE@nffPa916Bvu_V4#Epof105yNkw zzknJ50tl=DS->P)FK#X6%e&u3i|I74$@9uP0*YE!se={)r{r~?r68Z&3 zzZn^2KYiK`5I`&p3=IF7JHD>uaSofgtUAf*-w!6nU;qF7`S<(x|35&+uYZ4j|Njjl zS;T)|pU40ZKrEjoRWbQ1@P?oGFzcPCjLiCzcU8ZC{rmejNW<@6e}U*1kPT$~=Kk_~ z83O}A05QHjx|ikdd7h}_|NsB_wl9j+&d<}^`}fbEzkmP!1q4tEL@O#P9zA*#Ab?m{ zv`qe9Isj6Ah@Z*IdsTw}WYHgB8-D!*lmGvK*?)d>i2vThzyJ_HEdLn(@uk-N0|t-& z`$Yv&?#y3UfPVP*`ya#a|BS!>G5-DsFR)1c1&eHCHYDzO7ytqc0P0;>l>h9)WB>pF M07*qoM6N<$g71PE`~Uy| literal 0 HcmV?d00001 diff --git a/Mage.Common/src/mage/remote/Connection.java b/Mage.Common/src/mage/remote/Connection.java index 1ce44b0ef77..21e51faf794 100644 --- a/Mage.Common/src/mage/remote/Connection.java +++ b/Mage.Common/src/mage/remote/Connection.java @@ -58,6 +58,7 @@ public class Connection { private boolean showAbilityPickerForced; private boolean allowRequestShowHandCards; private boolean confirmEmptyManaPool; + private String flagName; private UserSkipPrioritySteps userSkipPrioritySteps; private static final String serialization = "?serializationtype=jboss"; @@ -269,4 +270,12 @@ public class Connection { this.forceDBComparison = forceDBComparison; } + public String getFlagName() { + return flagName; + } + + public void setFlagName(String flagName) { + this.flagName = flagName; + } + } diff --git a/Mage.Common/src/mage/remote/SessionImpl.java b/Mage.Common/src/mage/remote/SessionImpl.java index 702857db496..2bcc00dfb39 100644 --- a/Mage.Common/src/mage/remote/SessionImpl.java +++ b/Mage.Common/src/mage/remote/SessionImpl.java @@ -279,7 +279,8 @@ public class SessionImpl implements Session { connection.isShowAbilityPickerForced(), connection.allowRequestShowHandCards(), connection.confirmEmptyManaPool(), - connection.getUserSkipPrioritySteps()); + connection.getUserSkipPrioritySteps(), + connection.getFlagName()); // for backward compatibility. don't remove twice call - first one does nothing but for version checking registerResult = server.registerClient(connection.getUsername(), sessionId, client.getVersion()); if (registerResult) { @@ -1396,10 +1397,10 @@ public class SessionImpl implements Session { } @Override - public boolean updatePreferencesForServer(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps) { + public boolean updatePreferencesForServer(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps, String flagName) { try { if (isConnected()) { - UserDataView userDataView = new UserDataView(avatarId, showAbilityPickerForced, allowRequestShowHandCards, confirmEmptyManaPool, userSkipPrioritySteps); + UserDataView userDataView = new UserDataView(avatarId, showAbilityPickerForced, allowRequestShowHandCards, confirmEmptyManaPool, userSkipPrioritySteps, flagName); server.setUserData(connection.getUsername(), sessionId, userDataView); } return true; diff --git a/Mage.Common/src/mage/remote/interfaces/ClientData.java b/Mage.Common/src/mage/remote/interfaces/ClientData.java index 64242130e99..2a9fe56b384 100644 --- a/Mage.Common/src/mage/remote/interfaces/ClientData.java +++ b/Mage.Common/src/mage/remote/interfaces/ClientData.java @@ -36,5 +36,5 @@ public interface ClientData { String getUserName(); - boolean updatePreferencesForServer(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps); + boolean updatePreferencesForServer(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps, String flagName); } diff --git a/Mage.Common/src/mage/view/PlayerView.java b/Mage.Common/src/mage/view/PlayerView.java index 5ed57121f84..3e9eb437d81 100644 --- a/Mage.Common/src/mage/view/PlayerView.java +++ b/Mage.Common/src/mage/view/PlayerView.java @@ -115,7 +115,7 @@ public class PlayerView implements Serializable { if (player.getUserData() != null) { this.userDataView = new UserDataView(player.getUserData()); } else { - this.userDataView = new UserDataView(0, false, false, true, null); + this.userDataView = new UserDataView(0, false, false, true, null,"world.png"); } for (CommandObject commandObject : game.getState().getCommand()) { diff --git a/Mage.Common/src/mage/view/UserDataView.java b/Mage.Common/src/mage/view/UserDataView.java index d1e17f3884c..9bf65b6f2e7 100644 --- a/Mage.Common/src/mage/view/UserDataView.java +++ b/Mage.Common/src/mage/view/UserDataView.java @@ -17,14 +17,16 @@ public class UserDataView implements Serializable { protected boolean allowRequestShowHandCards; protected boolean confirmEmptyManaPool; protected UserSkipPrioritySteps userSkipPrioritySteps; + String flagName; public UserDataView(int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, - boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps) { + boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps, String flagName) { this.avatarId = avatarId; this.showAbilityPickerForced = showAbilityPickerForced; this.allowRequestShowHandCards = allowRequestShowHandCards; this.userSkipPrioritySteps = userSkipPrioritySteps; this.confirmEmptyManaPool = confirmEmptyManaPool; + this.flagName = flagName; } public UserDataView(UserData userData) { @@ -34,6 +36,7 @@ public class UserDataView implements Serializable { this.showAbilityPickerForced = userData.isShowAbilityPickerForced(); this.userSkipPrioritySteps = userData.getUserSkipPrioritySteps(); this.confirmEmptyManaPool = userData.confirmEmptyManaPool(); + this.flagName = userData.getFlagName(); } public int getAvatarId() { @@ -55,5 +58,9 @@ public class UserDataView implements Serializable { public boolean confirmEmptyManaPool() { return confirmEmptyManaPool; } + + public String getFlagName() { + return flagName; + } } diff --git a/Mage.Common/src/mage/view/UsersView.java b/Mage.Common/src/mage/view/UsersView.java index 1a7a28ec978..c5e8522bec4 100644 --- a/Mage.Common/src/mage/view/UsersView.java +++ b/Mage.Common/src/mage/view/UsersView.java @@ -37,18 +37,24 @@ public class UsersView implements Serializable { private static final long serialVersionUID = 1L; + private final String flagName; private final String userName; private final String infoState; private final String infoGames; private final String infoPing; - public UsersView(String userName, String infoState, String infoGames, String infoPing) { + public UsersView(String flagName, String userName, String infoState, String infoGames, String infoPing) { + this.flagName = flagName; this.userName = userName; this.infoState = infoState; this.infoGames = infoGames; this.infoPing = infoPing; } + public String getFlagName() { + return flagName; + } + public String getUserName() { return userName; } diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 6e7e2e41228..e10917f2746 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -175,7 +175,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { public ComputerPlayer(String name, RangeOfInfluence range) { super(name, range); human = false; - userData = new UserData(UserGroup.COMPUTER, 64, false, true, false, null); + userData = new UserData(UserGroup.COMPUTER, 64, false, true, false, null, "Computer.png"); pickedCards = new ArrayList<>(); } diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 73701d39fa4..2560f43da87 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -142,7 +142,7 @@ public class Session { if (user == null) { user = UserManager.getInstance().findUser("Admin"); } - user.setUserData(new UserData(UserGroup.ADMIN, 0, false, false, false, null)); + user.setUserData(new UserData(UserGroup.ADMIN, 0, false, false, false, null, "world.png")); if (!UserManager.getInstance().connectToSession(sessionId, user.getId())) { logger.info("Error connecting Admin!"); } @@ -156,7 +156,8 @@ public class Session { if (userData == null) { userData = new UserData(UserGroup.PLAYER, userDataView.getAvatarId(), userDataView.isShowAbilityPickerForced(), userDataView.allowRequestShowHandCards(), - userDataView.confirmEmptyManaPool(), userDataView.getUserSkipPrioritySteps()); + userDataView.confirmEmptyManaPool(), userDataView.getUserSkipPrioritySteps(), + userDataView.getFlagName()); user.setUserData(userData); } else { if (userDataView.getAvatarId() == 51) { // Update special avatar if first avatar is selected diff --git a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java index 5ce4d40e853..55c92050658 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java @@ -120,10 +120,10 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable { List users = new ArrayList<>(); for (User user : UserManager.getInstance().getUsers()) { try { - users.add(new UsersView(user.getName(), user.getInfo(), user.getGameInfo(), user.getPingInfo())); + users.add(new UsersView(user.getUserData().getFlagName(), user.getName(), user.getInfo(), user.getGameInfo(), user.getPingInfo())); } catch (Exception ex) { logger.fatal("User update exception: " + user.getName() + " - " + ex.toString(), ex); - users.add(new UsersView(user.getName(), user.getInfo(), "[exception]", user.getPingInfo())); + users.add(new UsersView(user.getUserData().getFlagName(), user.getName(), user.getInfo(), "[exception]", user.getPingInfo())); } } diff --git a/Mage/src/mage/players/net/UserData.java b/Mage/src/mage/players/net/UserData.java index 71090e3e18c..72823bd53ea 100644 --- a/Mage/src/mage/players/net/UserData.java +++ b/Mage/src/mage/players/net/UserData.java @@ -15,15 +15,18 @@ public class UserData implements Serializable { protected boolean allowRequestShowHandCards; protected boolean confirmEmptyManaPool; protected UserSkipPrioritySteps userSkipPrioritySteps; + protected String flagName; public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced, - boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps) { + boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps, + String flagName) { this.groupId = userGroup.getGroupId(); this.avatarId = avatarId; this.showAbilityPickerForced = showAbilityPickerForced; this.allowRequestShowHandCards = allowRequestShowHandCards; this.userSkipPrioritySteps = userSkipPrioritySteps; this.confirmEmptyManaPool = confirmEmptyManaPool; + this.flagName = flagName; } public void setGroupId(int groupId) { @@ -77,5 +80,9 @@ public class UserData implements Serializable { public void setConfirmEmptyManaPool(boolean confirmEmptyManaPool) { this.confirmEmptyManaPool = confirmEmptyManaPool; } + + public String getFlagName() { + return flagName; + } } From 132ded19f6c9f2172062c8f6e1000e6d823eaec8 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 11 Jun 2015 00:11:40 +0200 Subject: [PATCH 090/102] Show country flag icon in user table. Save and restore column width and order. --- .../main/java/mage/client/chat/ChatPanel.java | 50 +- .../mage/client/dialog/ConnectDialog.java | 7 +- .../mage/client/dialog/PreferencesDialog.java | 4 + .../java/mage/client/table/TablesPanel.java | 63 +-- .../java/mage/client/util/gui/TableUtil.java | 87 +++ .../util/gui/countryBox/CountryComboBox.java | 497 +++++++++--------- .../gui/countryBox/CountryItemEditor.java | 14 +- .../main/java/org/mage/card/arcane/Util.java | 16 +- 8 files changed, 418 insertions(+), 320 deletions(-) create mode 100644 Mage.Client/src/main/java/mage/client/util/gui/TableUtil.java diff --git a/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java b/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java index ff2955e0fec..60a405a7928 100644 --- a/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java +++ b/Mage.Client/src/main/java/mage/client/chat/ChatPanel.java @@ -37,20 +37,31 @@ import java.awt.Color; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; +import javax.swing.Icon; +import javax.swing.ImageIcon; import javax.swing.JTextField; import javax.swing.table.AbstractTableModel; import javax.swing.table.JTableHeader; import javax.swing.table.TableColumnModel; import mage.client.MageFrame; +import mage.client.dialog.PreferencesDialog; +import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_ORDER; +import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH; +import static mage.client.dialog.PreferencesDialog.KEY_USERS_COLUMNS_ORDER; +import static mage.client.dialog.PreferencesDialog.KEY_USERS_COLUMNS_WIDTH; import mage.client.util.MageTableRowSorter; +import mage.client.util.gui.TableUtil; import mage.remote.MageRemoteException; import mage.remote.Session; import mage.view.ChatMessage.MessageColor; import mage.view.ChatMessage.MessageType; import mage.view.RoomUsersView; import mage.view.UsersView; +import org.apache.log4j.Logger; import org.mage.card.arcane.ManaSymbols; /** @@ -58,7 +69,9 @@ import org.mage.card.arcane.ManaSymbols; * @author BetaSteward_at_googlemail.com, nantuko */ public class ChatPanel extends javax.swing.JPanel { - + + private static final Logger logger = Logger.getLogger(ChatPanel.class); + private UUID chatId; private Session session; private final List players = new ArrayList<>(); @@ -117,6 +130,8 @@ public class ChatPanel extends javax.swing.JPanel { */ private ChatType chatType = ChatType.DEFAULT; + private static final int[] defaultColumnsWidth = {20, 100, 100, 80}; + public enum ChatType { DEFAULT, GAME, TABLES, TOURNAMENT @@ -144,6 +159,9 @@ public class ChatPanel extends javax.swing.JPanel { jTablePlayers.setBackground(new Color(0, 0, 0, ALPHA)); jTablePlayers.setForeground(Color.white); jTablePlayers.setRowSorter(new MageTableRowSorter(tableModel)); + + TableUtil.setColumnWidthAndOrder(jTablePlayers, defaultColumnsWidth, KEY_USERS_COLUMNS_WIDTH, KEY_USERS_COLUMNS_ORDER); + if (jScrollPaneTxt != null) { jScrollPaneTxt.setBackground(new Color(0, 0, 0, ALPHA)); jScrollPaneTxt.getViewport().setBackground(new Color(0, 0, 0, ALPHA)); @@ -157,6 +175,10 @@ public class ChatPanel extends javax.swing.JPanel { } } + public void cleanUp() { + TableUtil.saveColumnWidthAndOrderToPrefs(jTablePlayers, KEY_USERS_COLUMNS_WIDTH, KEY_USERS_COLUMNS_ORDER); + } + public ChatType getChatType() { return chatType; } @@ -311,8 +333,11 @@ public class ChatPanel extends javax.swing.JPanel { class TableModel extends AbstractTableModel { + + private final String[] columnNames = new String[]{" ","Players", "Info", "Games", "Connection"}; private UsersView[] players = new UsersView[0]; + private Map flagIconCache = new HashMap<>(); public void loadData(Collection roomUserInfoList) throws MageRemoteException { RoomUsersView roomUserInfo = roomUserInfoList.iterator().next(); @@ -343,7 +368,7 @@ public class ChatPanel extends javax.swing.JPanel { public Object getValueAt(int arg0, int arg1) { switch (arg1) { case 0: - return players[arg0].getFlagName(); + return getCountryFlagIcon(players[arg0].getFlagName()); case 1: return players[arg0].getUserName(); case 2: @@ -369,13 +394,32 @@ public class ChatPanel extends javax.swing.JPanel { @Override public Class getColumnClass(int columnIndex) { - return String.class; + switch (columnIndex) { + case 0: + return Icon.class; + default: + return String.class; + } } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } + + private ImageIcon getCountryFlagIcon(String countryCode) { + ImageIcon flagIcon = flagIconCache.get(countryCode); + if (flagIcon == null) { + flagIcon = new javax.swing.ImageIcon(getClass().getResource("/flags/" + countryCode +".png")); + if (flagIcon.getImage() == null) { + logger.warn("Country flag resource not found: " + countryCode); + } else { + flagIconCache.put(countryCode, flagIcon); + } + } + return flagIcon; + } + } public void clear() { diff --git a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index d26d235edd8..2e53ee5cdd4 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -65,6 +65,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static mage.client.dialog.PreferencesDialog.KEY_CONNECT_AUTO_CONNECT; import static mage.client.dialog.PreferencesDialog.KEY_CONNECT_FLAG; +import mage.client.util.gui.countryBox.CountryItemEditor; import mage.remote.Connection.ProxyType; /** @@ -101,7 +102,7 @@ public class ConnectDialog extends MageDialog { this.chkAutoConnect.setSelected(Boolean.parseBoolean(MageFrame.getPreferences().get(KEY_CONNECT_AUTO_CONNECT, "false"))); this.chkForceUpdateDB.setSelected(false); // has always to be set manually to force comparison - String selectedFlag = MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world.png"); + String selectedFlag = MageFrame.getPreferences().get(KEY_CONNECT_FLAG, "world"); // set the selected country/flag for (int i = 0; i < cbFlag.getItemCount(); i++) { String[] name = (String[])cbFlag.getItemAt(i); @@ -292,7 +293,7 @@ public class ConnectDialog extends MageDialog { private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed MageFrame.getPreferences().put("autoConnect", Boolean.toString(chkAutoConnect.isSelected())); - MageFrame.getPreferences().put(KEY_CONNECT_FLAG, (String)cbFlag.getSelectedItem()); + MageFrame.getPreferences().put(KEY_CONNECT_FLAG, ((CountryItemEditor)cbFlag.getEditor()).getImageItem()); if (task != null && !task.isDone()) { task.cancel(true); } else { @@ -328,7 +329,7 @@ public class ConnectDialog extends MageDialog { connection.setPort(Integer.valueOf(this.txtPort.getText().trim())); connection.setUsername(this.txtUserName.getText().trim()); connection.setForceDBComparison(this.chkForceUpdateDB.isSelected()); - MageFrame.getPreferences().put(KEY_CONNECT_FLAG, (String)cbFlag.getSelectedItem()); + MageFrame.getPreferences().put(KEY_CONNECT_FLAG, ((CountryItemEditor)cbFlag.getEditor()).getImageItem()); ProxyType configProxyType = Connection.ProxyType.valueByText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_TYPE, "None")); diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index e33b9c0b92d..75d8bf82b62 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -150,6 +150,10 @@ public class PreferencesDialog extends javax.swing.JDialog { public static final String KEY_TABLES_DIVIDER_LOCATION_2 = "tablePanelDividerLocation2"; public static final String KEY_TABLES_DIVIDER_LOCATION_3 = "tablePanelDividerLocation3"; + // user list + public static final String KEY_USERS_COLUMNS_WIDTH = "userPanelColumnWidth"; + public static final String KEY_USERS_COLUMNS_ORDER = "userPanelColumnSort"; + public static final String KEY_GAMEPANEL_DIVIDER_LOCATION_0 = "gamepanelDividerLocation0"; public static final String KEY_GAMEPANEL_DIVIDER_LOCATION_1 = "gamepanelDividerLocation1"; public static final String KEY_GAMEPANEL_DIVIDER_LOCATION_2 = "gamepanelDividerLocation2"; diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index c9440f01e6e..62b0294b4cb 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -64,12 +64,10 @@ import javax.swing.JInternalFrame; import javax.swing.JLayeredPane; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; -import javax.swing.JTable; import javax.swing.JToggleButton; import javax.swing.RowFilter; import javax.swing.SwingWorker; import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableColumn; import mage.cards.decks.importer.DeckImporterUtil; import mage.client.MageFrame; import mage.client.chat.ChatPanel; @@ -78,10 +76,13 @@ import mage.client.dialog.JoinTableDialog; import mage.client.dialog.NewTableDialog; import mage.client.dialog.NewTournamentDialog; import mage.client.dialog.PreferencesDialog; +import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_ORDER; +import static mage.client.dialog.PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH; import mage.client.dialog.TableWaitingDialog; import mage.client.util.ButtonColumn; import mage.client.util.MageTableRowSorter; import mage.client.util.gui.GuiDisplayUtil; +import mage.client.util.gui.TableUtil; import mage.constants.MatchTimeLimit; import mage.constants.MultiplayerAttackOption; import mage.constants.RangeOfInfluence; @@ -93,7 +94,6 @@ import mage.view.MatchView; import mage.view.RoomUsersView; import mage.view.TableView; import org.apache.log4j.Logger; -import org.mage.card.arcane.Util; /** * @@ -120,9 +120,11 @@ public class TablesPanel extends javax.swing.JPanel { JToggleButton[] filterButtons; + private static final int[] defaultColumnsWidth = {35, 150, 120, 180, 80, 120, 80, 60, 60}; + /** Creates new form TablesPanel */ public TablesPanel() { - + tableModel = new TableTableModel(); matchesModel = new MatchesTableModel(); gameChooser = new GameChooser(); @@ -135,7 +137,8 @@ public class TablesPanel extends javax.swing.JPanel { activeTablesSorter = new MageTableRowSorter(tableModel); tableTables.setRowSorter(activeTablesSorter); - TableTableModel.setColumnWidthAndOrder(tableTables); + TableUtil.setColumnWidthAndOrder(tableTables, defaultColumnsWidth, + PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH, PreferencesDialog.KEY_TABLES_COLUMNS_ORDER); tableCompleted.setRowSorter(new MageTableRowSorter(matchesModel)); @@ -276,6 +279,7 @@ public class TablesPanel extends javax.swing.JPanel { public void cleanUp() { saveSettings(); + chatPanel.cleanUp(); } private void saveDividerLocations() { @@ -311,23 +315,7 @@ public class TablesPanel extends javax.swing.JPanel { } PreferencesDialog.saveValue(PreferencesDialog.KEY_TABLES_FILTER_SETTINGS, formatSettings.toString()); - // Column width - StringBuilder columnWidthSettings = new StringBuilder(); - StringBuilder columnOrderSettings = new StringBuilder(); - boolean firstValue = true; - for (int i = 0; i < tableTables.getColumnModel().getColumnCount(); i++) { - TableColumn column = tableTables.getColumnModel().getColumn(tableTables.convertColumnIndexToView(i)); - if (!firstValue) { - columnWidthSettings.append(","); - columnOrderSettings.append(","); - } else { - firstValue = false; - } - columnWidthSettings.append(column.getWidth()); - columnOrderSettings.append(tableTables.convertColumnIndexToModel(i)); - } - PreferencesDialog.saveValue(PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH, columnWidthSettings.toString()); - PreferencesDialog.saveValue(PreferencesDialog.KEY_TABLES_COLUMNS_ORDER, columnOrderSettings.toString()); + TableUtil.saveColumnWidthAndOrderToPrefs(tableTables, KEY_TABLES_COLUMNS_WIDTH, KEY_TABLES_COLUMNS_ORDER); } private void restoreDividerLocations() { @@ -1157,6 +1145,7 @@ public class TablesPanel extends javax.swing.JPanel { options.setWinsNeeded(1); options.setMatchTimeLimit(MatchTimeLimit.NONE); options.setFreeMulligans(2); + options.setSkillLevel(SkillLevel.CASUAL); table = session.createTable(roomId, options); session.joinTable(roomId, table.getTableId(), "Human", "Human", 1, DeckImporterUtil.importDeck("test.dck"),""); @@ -1262,8 +1251,7 @@ class TableTableModel extends AbstractTableModel { public static final int ACTION_COLUMN = 8; // column the action is located (starting with 0) private final String[] columnNames = new String[]{"M/T","Deck Type", "Owner / Players", "Game Type", "Info", "Status", "Created / Started", "Skill Level", "Action"}; - private static final int[] defaultColumnsWidth = {35, 150, 120, 180, 80, 120, 80, 60, 60}; - + private TableView[] tables = new TableView[0]; private static final DateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss");; @@ -1272,32 +1260,7 @@ class TableTableModel extends AbstractTableModel { public void loadData(Collection tables) throws MageRemoteException { this.tables = tables.toArray(new TableView[0]); this.fireTableDataChanged(); - } - - static public void setColumnWidthAndOrder(JTable table) { - table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); - - // set the column width from saved value or defaults - int[] widths = Util.getIntArrayFromString(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_TABLES_COLUMNS_WIDTH, null)); - int i = 0; - for (int width : defaultColumnsWidth) { - if (widths != null && widths.length > i) { - width = widths[i]; - } - TableColumn column = table.getColumnModel().getColumn(i++); - column.setWidth(width); - column.setPreferredWidth(width); - } - - // set the column order - int[] order = Util.getIntArrayFromString(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_TABLES_COLUMNS_ORDER, null)); - if (order != null && order.length == table.getColumnCount()) { - for (int j = 0; j < table.getColumnCount(); j++) { - table.moveColumn(table.convertColumnIndexToView(order[j]), j); - } - } - - } + } @Override public int getRowCount() { diff --git a/Mage.Client/src/main/java/mage/client/util/gui/TableUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/TableUtil.java new file mode 100644 index 00000000000..187453aa5e1 --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/util/gui/TableUtil.java @@ -0,0 +1,87 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package mage.client.util.gui; + +import javax.swing.JTable; +import javax.swing.table.TableColumn; +import mage.client.dialog.PreferencesDialog; +import org.mage.card.arcane.Util; + +/** + * + * @author LevelX2 + */ +public class TableUtil { + + /** + * + * @param table + * @param defaultColumnsWidth + * @param widthPrefKey + * @param orderPrefKey + */ + static public void setColumnWidthAndOrder(JTable table, int[] defaultColumnsWidth, String widthPrefKey, String orderPrefKey) { + table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); + + // set the column width from saved value or defaults + int[] widths = getIntArrayFromString(PreferencesDialog.getCachedValue(widthPrefKey, null)); + int i = 0; + for (int width : defaultColumnsWidth) { + if (widths != null && widths.length > i) { + width = widths[i]; + } + TableColumn column = table.getColumnModel().getColumn(i++); + column.setWidth(width); + column.setPreferredWidth(width); + } + + // set the column order + int[] order = getIntArrayFromString(PreferencesDialog.getCachedValue(orderPrefKey, null)); + if (order != null && order.length == table.getColumnCount()) { + for (int j = 0; j < table.getColumnCount(); j++) { + table.moveColumn(table.convertColumnIndexToView(order[j]), j); + } + } + + } + + static public void saveColumnWidthAndOrderToPrefs(JTable table, String widthPrefKey, String orderPrefKey) { + // Column width + StringBuilder columnWidthSettings = new StringBuilder(); + StringBuilder columnOrderSettings = new StringBuilder(); + boolean firstValue = true; + for (int i = 0; i < table.getColumnModel().getColumnCount(); i++) { + TableColumn column = table.getColumnModel().getColumn(table.convertColumnIndexToView(i)); + if (!firstValue) { + columnWidthSettings.append(","); + columnOrderSettings.append(","); + } else { + firstValue = false; + } + columnWidthSettings.append(column.getWidth()); + columnOrderSettings.append(table.convertColumnIndexToModel(i)); + } + PreferencesDialog.saveValue(widthPrefKey, columnWidthSettings.toString()); + PreferencesDialog.saveValue(orderPrefKey, columnOrderSettings.toString()); + + } + + + public static int[] getIntArrayFromString(String stringData) { + int[] intArray = null; + if (stringData != null && !stringData.isEmpty()) { + String[] items = stringData.split(","); + int lengthW = items.length; + intArray = new int[lengthW]; + for (int i = 0; i < lengthW; i++) { + try { + intArray[i] = Integer.parseInt(items[i]); + } catch (NumberFormatException nfe) {} + } + } + return intArray; + } +} diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryComboBox.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryComboBox.java index 2c365d95e3c..d9687d13261 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryComboBox.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryComboBox.java @@ -40,252 +40,252 @@ public class CountryComboBox extends JComboBox { private final DefaultComboBoxModel model; public static String[][] countryList = { - {"Afghanistan", "af.png"}, - {"Åland Islands", "ax.png"}, - {"Albania", "al.png"}, - {"Algeria", "dz.png"}, - {"American Samoa", "as.png"}, - {"Andorra", "ad.png"}, - {"Angola", "an.png"}, - {"Anguilla", "ai.png"}, - {"Antarctica", "ao.png"}, - {"Antigua and Barbuda", "ag.png"}, - {"Argentina", "ar.png"}, - {"Armenia", "am.png"}, - {"Aruba", "aw.png"}, - {"Australia", "au.png"}, - {"Austria", "at.png"}, - {"Azerbaijan", "az.png"}, - {"Bahamas", "bs.png"}, - {"Bahrain", "bh.png"}, - {"Bangladesh", "bd.png"}, - {"Barbados", "bb.png"}, - {"Belarus", "by.png"}, - {"Belgium", "be.png"}, - {"Belize", "bz.png"}, - {"Benin", "bj.png"}, - {"Bermuda", "bm.png"}, - {"Bhutan", "bt.png"}, - {"Bolivia, Plurinational State of", "bo.png"}, - {"Bosnia and Herzegovina", "ba.png"}, - {"Botswana", "bw.png"}, - {"Bouvet Island", "bv.png"}, - {"Brazil", "br.png"}, - {"British Indian Ocean Territory", "io.png"}, - {"Brunei Darussalam", "bn.png"}, - {"Bulgaria", "bg.png"}, - {"Burkina Faso", "bf.png"}, - {"Burundi", "bi.png"}, - {"Cabo Verde", "cv.png"}, - {"Cambodia", "kh.png"}, - {"Cameroon", "cm.png"}, - {"Canada", "ca.png"}, - {"Catalonia", "catalonia.png"}, - {"Cayman Islands", "ky.png"}, - {"Central African Republic", "cf.png"}, - {"Chad", "td.png"}, - {"Chile", "cl.png"}, - {"China", "cn.png"}, - {"Christmas Island", "cx.png"}, - {"Cocos (Keeling) Islands", "cc.png"}, - {"Colombia", "co.png"}, - {"Comoros", "km.png"}, - {"Congo", "cg.png"}, - {"Congo, the Democratic Republic of the", "cd.png"}, - {"Cook Islands", "ck.png"}, - {"Costa Rica", "cr.png"}, - {"Côte d'Ivoire", "ci.png"}, - {"Croatia", "hr.png"}, - {"Cuba", "cu.png"}, - {"Cyprus", "cy.png"}, - {"Czech Republic", "cz.png"}, - {"Denmark", "dk.png"}, - {"Djibouti", "dj.png"}, - {"Dominica", "dm.png"}, - {"Dominican Republic", "do.png"}, - {"Ecuador", "ec.png"}, - {"Egypt", "eg.png"}, - {"El Salvador", "sv.png"}, - {"England", "england.png"}, - {"Equatorial Guinea", "gq.png"}, - {"Eritrea", "er.png"}, - {"Estonia", "ee.png"}, - {"Ethiopia", "et.png"}, - {"European Union", "europeanunion.png"}, - {"Falkland Islands (Malvinas)", "fk.png"}, - {"Faroe Islands", "fo.png"}, - {"Fiji", "fj.png"}, - {"Finland", "fi.png"}, - {"France", "fr.png"}, - {"French Guiana", "gf.png"}, - {"French Polynesia", "pf.png"}, - {"French Southern Territories", "tf.png"}, - {"Gabon", "ga.png"}, - {"Gambia", "gm.png"}, - {"Georgia", "ge.png"}, - {"Germany", "de.png"}, - {"Ghana", "gh.png"}, - {"Gibraltar", "gi.png"}, - {"Greece", "gr.png"}, - {"Greenland", "gl.png"}, - {"Grenada", "gd.png"}, - {"Guadeloupe", "gp.png"}, - {"Guam", "gu.png"}, - {"Guatemala", "gt.png"}, - {"Guinea", "gn.png"}, - {"Guinea-Bissau", "gw.png"}, - {"Guyana", "gy.png"}, - {"Haiti", "ht.png"}, - {"Heard Island and McDonald Islands", "hm.png"}, - {"Holy See", "va.png"}, - {"Honduras", "hn.png"}, - {"Hong Kong", "hk.png"}, - {"Hungary", "hu.png"}, - {"Iceland", "is.png"}, - {"India", "in.png"}, - {"Indonesia", "id.png"}, - {"Iran, Islamic Republic of", "ir.png"}, - {"Iraq", "iq.png"}, - {"Ireland", "ie.png"}, - {"Israel", "il.png"}, - {"Italy", "it.png"}, - {"Jamaica", "jm.png"}, - {"Japan", "jp.png"}, - {"Jordan", "jo.png"}, - {"Kazakhstan", "kz.png"}, - {"Kenya", "ke.png"}, - {"Kiribati", "ki.png"}, - {"Korea, Democratic People's Republic of", "kp.png"}, - {"Korea, Republic of", "kr.png"}, - {"Kuwait", "kw.png"}, - {"Kyrgyzstan", "kg.png"}, - {"Lao People's Democratic Republic", "la.png"}, - {"Latvia", "lv.png"}, - {"Lebanon", "lb.png"}, - {"Lesotho", "ls.png"}, - {"Liberia", "lr.png"}, - {"Libya", "ly.png"}, - {"Liechtenstein", "li.png"}, - {"Lithuania", "lt.png"}, - {"Luxembourg", "lu.png"}, - {"Macao", "mo.png"}, - {"Macedonia, the former Yugoslav Republic of", "mk.png"}, - {"Madagascar", "mg.png"}, - {"Malawi", "mw.png"}, - {"Malaysia", "my.png"}, - {"Maldives", "mv.png"}, - {"Mali", "ml.png"}, - {"Malta", "mt.png"}, - {"Marshall Islands", "mh.png"}, - {"Martinique", "mq.png"}, - {"Mauritania", "mr.png"}, - {"Mauritius", "mu.png"}, - {"Mayotte", "yt.png"}, - {"Mexico", "mx.png"}, - {"Micronesia, Federated States of", "fm.png"}, - {"Moldova, Republic of", "md.png"}, - {"Monaco", "mc.png"}, - {"Mongolia", "mn.png"}, - {"Montenegro", "me.png"}, - {"Montserrat", "ms.png"}, - {"Morocco", "ma.png"}, - {"Mozambique", "mz.png"}, - {"Myanmar", "mm.png"}, - {"Namibia", "na.png"}, - {"Nauru", "nr.png"}, - {"Nepal", "np.png"}, - {"Netherlands", "nl.png"}, - {"New Caledonia", "nc.png"}, - {"New Zealand", "nz.png"}, - {"Nicaragua", "ni.png"}, - {"Niger", "ne.png"}, - {"Nigeria", "ng.png"}, - {"Niue", "nu.png"}, - {"Norfolk Island", "nf.png"}, - {"Northern Mariana Islands", "mp.png"}, - {"Norway", "no.png"}, - {"Oman", "om.png"}, - {"Pakistan", "pk.png"}, - {"Palau", "pw.png"}, - {"Palestine, State of", "ps.png"}, - {"Panama", "pa.png"}, - {"Papua New Guinea", "pg.png"}, - {"Paraguay", "py.png"}, - {"Peru", "pe.png"}, - {"Philippines", "ph.png"}, - {"Pitcairn", "pn.png"}, - {"Poland", "pl.png"}, - {"Portugal", "pt.png"}, - {"Puerto Rico", "pr.png"}, - {"Qatar", "qa.png"}, - {"Réunion", "re.png"}, - {"Romania", "ro.png"}, - {"Russian Federation", "ru.png"}, - {"Rwanda", "rw.png"}, - {"Saint Helena, Ascension and Tristan da Cunha", "sh.png"}, - {"Saint Kitts and Nevis", "kn.png"}, - {"Saint Lucia", "lc.png"}, - {"Saint Pierre and Miquelon", "pm.png"}, - {"Saint Vincent and the Grenadines", "vc.png"}, - {"Samoa", "ws.png"}, - {"San Marino", "sm.png"}, - {"Sao Tome and Principe", "st.png"}, - {"Saudi Arabia", "sa.png"}, - {"Scotland", "scotland.png"}, - {"Senegal", "sn.png"}, - {"Serbia", "rs.png"}, - {"Seychelles", "sc.png"}, - {"Sierra Leone", "sl.png"}, - {"Singapore", "sg.png"}, - {"Slovakia", "sk.png"}, - {"Slovenia", "si.png"}, - {"Solomon Islands", "sb.png"}, - {"Somalia", "so.png"}, - {"South Africa", "za.png"}, - {"South Georgia and the South Sandwich Islands", "gs.png"}, - {"Spain", "es.png"}, - {"Sri Lanka", "lk.png"}, - {"Sudan", "sd.png"}, - {"Suriname", "sr.png"}, - {"Svalbard and Jan Mayen", "sj.png"}, - {"Swaziland", "sz.png"}, - {"Sweden", "se.png"}, - {"Switzerland", "ch.png"}, - {"Syrian Arab Republic", "sy.png"}, - {"Taiwan, Province of China", "tw.png"}, - {"Tajikistan", "tj.png"}, - {"Tanzania, United Republic of", "tz.png"}, - {"Thailand", "th.png"}, - {"Timor-Leste", "tl.png"}, - {"Togo", "tg.png"}, - {"Tokelau", "tk.png"}, - {"Tonga", "to.png"}, - {"Trinidad and Tobago", "tt.png"}, - {"Tunisia", "tn.png"}, - {"Turkey", "tr.png"}, - {"Turkmenistan", "tm.png"}, - {"Turks and Caicos Islands", "tc.png"}, - {"Tuvalu", "tv.png"}, - {"Uganda", "ug.png"}, - {"Ukraine", "ua.png"}, - {"United Arab Emirates", "ae.png"}, - {"United Kingdom of Great Britain and Northern Ireland", "gb.png"}, - {"United States Minor Outlying Islands", "um.png"}, - {"United States of America", "us.png"}, - {"Uruguay", "uy.png"}, - {"Uzbekistan", "uz.png"}, - {"Vanuatu", "vu.png"}, - {"Venezuela, Bolivarian Republic of", "ve.png"}, - {"Viet Nam", "vn.png"}, - {"Virgin Islands, British", "vg.png"}, - {"Virgin Islands, U.S.", "vi.png"}, - {"Wales", "wales.png"}, - {"Wallis and Futuna", "wf.png"}, - {"World", "world.png"}, - {"Western Sahara", "eh.png"}, - {"Yemen", "ye.png"}, - {"Zambia", "zm.png"}, - {"Zimbabwe", "zw.png"}, + {"Afghanistan", "af"}, + {"Åland Islands", "ax"}, + {"Albania", "al"}, + {"Algeria", "dz"}, + {"American Samoa", "as"}, + {"Andorra", "ad"}, + {"Angola", "an"}, + {"Anguilla", "ai"}, + {"Antarctica", "ao"}, + {"Antigua and Barbuda", "ag"}, + {"Argentina", "ar"}, + {"Armenia", "am"}, + {"Aruba", "aw"}, + {"Australia", "au"}, + {"Austria", "at"}, + {"Azerbaijan", "az"}, + {"Bahamas", "bs"}, + {"Bahrain", "bh"}, + {"Bangladesh", "bd"}, + {"Barbados", "bb"}, + {"Belarus", "by"}, + {"Belgium", "be"}, + {"Belize", "bz"}, + {"Benin", "bj"}, + {"Bermuda", "bm"}, + {"Bhutan", "bt"}, + {"Bolivia, Plurinational State of", "bo"}, + {"Bosnia and Herzegovina", "ba"}, + {"Botswana", "bw"}, + {"Bouvet Island", "bv"}, + {"Brazil", "br"}, + {"British Indian Ocean Territory", "io"}, + {"Brunei Darussalam", "bn"}, + {"Bulgaria", "bg"}, + {"Burkina Faso", "bf"}, + {"Burundi", "bi"}, + {"Cabo Verde", "cv"}, + {"Cambodia", "kh"}, + {"Cameroon", "cm"}, + {"Canada", "ca"}, + {"Catalonia", "catalonia"}, + {"Cayman Islands", "ky"}, + {"Central African Republic", "cf"}, + {"Chad", "td"}, + {"Chile", "cl"}, + {"China", "cn"}, + {"Christmas Island", "cx"}, + {"Cocos (Keeling) Islands", "cc"}, + {"Colombia", "co"}, + {"Comoros", "km"}, + {"Congo", "cg"}, + {"Congo, the Democratic Republic of the", "cd"}, + {"Cook Islands", "ck"}, + {"Costa Rica", "cr"}, + {"Côte d'Ivoire", "ci"}, + {"Croatia", "hr"}, + {"Cuba", "cu"}, + {"Cyprus", "cy"}, + {"Czech Republic", "cz"}, + {"Denmark", "dk"}, + {"Djibouti", "dj"}, + {"Dominica", "dm"}, + {"Dominican Republic", "do"}, + {"Ecuador", "ec"}, + {"Egypt", "eg"}, + {"El Salvador", "sv"}, + {"England", "england"}, + {"Equatorial Guinea", "gq"}, + {"Eritrea", "er"}, + {"Estonia", "ee"}, + {"Ethiopia", "et"}, + {"European Union", "europeanunion"}, + {"Falkland Islands (Malvinas)", "fk"}, + {"Faroe Islands", "fo"}, + {"Fiji", "fj"}, + {"Finland", "fi"}, + {"France", "fr"}, + {"French Guiana", "gf"}, + {"French Polynesia", "pf"}, + {"French Southern Territories", "tf"}, + {"Gabon", "ga"}, + {"Gambia", "gm"}, + {"Georgia", "ge"}, + {"Germany", "de"}, + {"Ghana", "gh"}, + {"Gibraltar", "gi"}, + {"Greece", "gr"}, + {"Greenland", "gl"}, + {"Grenada", "gd"}, + {"Guadeloupe", "gp"}, + {"Guam", "gu"}, + {"Guatemala", "gt"}, + {"Guinea", "gn"}, + {"Guinea-Bissau", "gw"}, + {"Guyana", "gy"}, + {"Haiti", "ht"}, + {"Heard Island and McDonald Islands", "hm"}, + {"Holy See", "va"}, + {"Honduras", "hn"}, + {"Hong Kong", "hk"}, + {"Hungary", "hu"}, + {"Iceland", "is"}, + {"India", "in"}, + {"Indonesia", "id"}, + {"Iran, Islamic Republic of", "ir"}, + {"Iraq", "iq"}, + {"Ireland", "ie"}, + {"Israel", "il"}, + {"Italy", "it"}, + {"Jamaica", "jm"}, + {"Japan", "jp"}, + {"Jordan", "jo"}, + {"Kazakhstan", "kz"}, + {"Kenya", "ke"}, + {"Kiribati", "ki"}, + {"Korea, Democratic People's Republic of", "kp"}, + {"Korea, Republic of", "kr"}, + {"Kuwait", "kw"}, + {"Kyrgyzstan", "kg"}, + {"Lao People's Democratic Republic", "la"}, + {"Latvia", "lv"}, + {"Lebanon", "lb"}, + {"Lesotho", "ls"}, + {"Liberia", "lr"}, + {"Libya", "ly"}, + {"Liechtenstein", "li"}, + {"Lithuania", "lt"}, + {"Luxembourg", "lu"}, + {"Macao", "mo"}, + {"Macedonia, the former Yugoslav Republic of", "mk"}, + {"Madagascar", "mg"}, + {"Malawi", "mw"}, + {"Malaysia", "my"}, + {"Maldives", "mv"}, + {"Mali", "ml"}, + {"Malta", "mt"}, + {"Marshall Islands", "mh"}, + {"Martinique", "mq"}, + {"Mauritania", "mr"}, + {"Mauritius", "mu"}, + {"Mayotte", "yt"}, + {"Mexico", "mx"}, + {"Micronesia, Federated States of", "fm"}, + {"Moldova, Republic of", "md"}, + {"Monaco", "mc"}, + {"Mongolia", "mn"}, + {"Montenegro", "me"}, + {"Montserrat", "ms"}, + {"Morocco", "ma"}, + {"Mozambique", "mz"}, + {"Myanmar", "mm"}, + {"Namibia", "na"}, + {"Nauru", "nr"}, + {"Nepal", "np"}, + {"Netherlands", "nl"}, + {"New Caledonia", "nc"}, + {"New Zealand", "nz"}, + {"Nicaragua", "ni"}, + {"Niger", "ne"}, + {"Nigeria", "ng"}, + {"Niue", "nu"}, + {"Norfolk Island", "nf"}, + {"Northern Mariana Islands", "mp"}, + {"Norway", "no"}, + {"Oman", "om"}, + {"Pakistan", "pk"}, + {"Palau", "pw"}, + {"Palestine, State of", "ps"}, + {"Panama", "pa"}, + {"Papua New Guinea", "pg"}, + {"Paraguay", "py"}, + {"Peru", "pe"}, + {"Philippines", "ph"}, + {"Pitcairn", "pn"}, + {"Poland", "pl"}, + {"Portugal", "pt"}, + {"Puerto Rico", "pr"}, + {"Qatar", "qa"}, + {"Réunion", "re"}, + {"Romania", "ro"}, + {"Russian Federation", "ru"}, + {"Rwanda", "rw"}, + {"Saint Helena, Ascension and Tristan da Cunha", "sh"}, + {"Saint Kitts and Nevis", "kn"}, + {"Saint Lucia", "lc"}, + {"Saint Pierre and Miquelon", "pm"}, + {"Saint Vincent and the Grenadines", "vc"}, + {"Samoa", "ws"}, + {"San Marino", "sm"}, + {"Sao Tome and Principe", "st"}, + {"Saudi Arabia", "sa"}, + {"Scotland", "scotland"}, + {"Senegal", "sn"}, + {"Serbia", "rs"}, + {"Seychelles", "sc"}, + {"Sierra Leone", "sl"}, + {"Singapore", "sg"}, + {"Slovakia", "sk"}, + {"Slovenia", "si"}, + {"Solomon Islands", "sb"}, + {"Somalia", "so"}, + {"South Africa", "za"}, + {"South Georgia and the South Sandwich Islands", "gs"}, + {"Spain", "es"}, + {"Sri Lanka", "lk"}, + {"Sudan", "sd"}, + {"Suriname", "sr"}, + {"Svalbard and Jan Mayen", "sj"}, + {"Swaziland", "sz"}, + {"Sweden", "se"}, + {"Switzerland", "ch"}, + {"Syrian Arab Republic", "sy"}, + {"Taiwan, Province of China", "tw"}, + {"Tajikistan", "tj"}, + {"Tanzania, United Republic of", "tz"}, + {"Thailand", "th"}, + {"Timor-Leste", "tl"}, + {"Togo", "tg"}, + {"Tokelau", "tk"}, + {"Tonga", "to"}, + {"Trinidad and Tobago", "tt"}, + {"Tunisia", "tn"}, + {"Turkey", "tr"}, + {"Turkmenistan", "tm"}, + {"Turks and Caicos Islands", "tc"}, + {"Tuvalu", "tv"}, + {"Uganda", "ug"}, + {"Ukraine", "ua"}, + {"United Arab Emirates", "ae"}, + {"United Kingdom of Great Britain and Northern Ireland", "gb"}, + {"United States Minor Outlying Islands", "um"}, + {"United States of America", "us"}, + {"Uruguay", "uy"}, + {"Uzbekistan", "uz"}, + {"Vanuatu", "vu"}, + {"Venezuela, Bolivarian Republic of", "ve"}, + {"Viet Nam", "vn"}, + {"Virgin Islands, British", "vg"}, + {"Virgin Islands, U.S.", "vi"}, + {"Wales", "wales"}, + {"Wallis and Futuna", "wf"}, + {"World", "world"}, + {"Western Sahara", "eh"}, + {"Yemen", "ye"}, + {"Zambia", "zm"}, + {"Zimbabwe", "zw"}, }; @SuppressWarnings("unchecked") @@ -310,6 +310,11 @@ public class CountryComboBox extends JComboBox { model.addElement(anItem); } } + + @Override + public Object getSelectedItem() { + return super.getSelectedItem(); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemEditor.java b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemEditor.java index 11d6e66cc91..3bd0ee153d8 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemEditor.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/countryBox/CountryItemEditor.java @@ -35,6 +35,7 @@ import java.awt.Insets; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.border.LineBorder; import javax.swing.plaf.basic.BasicComboBoxEditor; @@ -47,20 +48,23 @@ public class CountryItemEditor extends BasicComboBoxEditor { private final JPanel panel = new JPanel(); private final JLabel labelItem = new JLabel(); private String selectedValue; + private String selectedImage; public CountryItemEditor() { panel.setLayout(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.weightx = 1.0; - constraints.insets = new Insets(2, 5, 2, 2); +// constraints.insets = new Insets(2, 5, 2, 2); + constraints.insets = new Insets(0, 5, 0, 0); labelItem.setOpaque(false); labelItem.setHorizontalAlignment(JLabel.LEFT); labelItem.setForeground(Color.WHITE); panel.add(labelItem, constraints); - panel.setBackground(new Color(0,104,139, 0)); +// panel.setBackground(Color.WHITE); + panel.setBackground(new Color(0, 100,190, 255)); selectedValue = null; } @@ -74,6 +78,9 @@ public class CountryItemEditor extends BasicComboBoxEditor { return this.selectedValue; } + public String getImageItem() { + return this.selectedImage; + } @Override public void setItem(Object item) { if (item == null || !(item instanceof String[])) { @@ -81,7 +88,8 @@ public class CountryItemEditor extends BasicComboBoxEditor { } String[] countryItem = (String[]) item; selectedValue = countryItem[0]; + selectedImage = countryItem[1]; labelItem.setText(selectedValue); - labelItem.setIcon(new ImageIcon(getClass().getResource("/flags/"+ countryItem[1]))); + labelItem.setIcon(new ImageIcon(getClass().getResource("/flags/"+ countryItem[1] + ".png"))); } } diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/Util.java b/Mage.Client/src/main/java/org/mage/card/arcane/Util.java index 6e608945d3d..28b26feed93 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/Util.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/Util.java @@ -89,19 +89,5 @@ public class Util { throw new RuntimeException("Error invoking runnable in UI thread.", ex); } } - - public static int[] getIntArrayFromString(String stringData) { - int[] intArray = null; - if (stringData != null && !stringData.isEmpty()) { - String[] items = stringData.split(","); - int lengthW = items.length; - intArray = new int[lengthW]; - for (int i = 0; i < lengthW; i++) { - try { - intArray[i] = Integer.parseInt(items[i]); - } catch (NumberFormatException nfe) {} - } - } - return intArray; - } + } From 50250176ed37ddc235d5ff7f502e3695a5e98a1f Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 11 Jun 2015 00:16:38 +0200 Subject: [PATCH 091/102] * Recurring Insight - FIxed target handling. --- .../sets/riseoftheeldrazi/RecurringInsight.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/RecurringInsight.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/RecurringInsight.java index 4745f6eef18..564afe4c19a 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/RecurringInsight.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/RecurringInsight.java @@ -49,13 +49,13 @@ public class RecurringInsight extends CardImpl { public RecurringInsight(UUID ownerId) { super(ownerId, 82, "Recurring Insight", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{4}{U}{U}"); this.expansionSetCode = "ROE"; - // Rebound this.addAbility(new ReboundAbility()); // Draw cards equal to the number of cards in target opponent's hand. this.getSpellAbility().addEffect(new RecurringInsightEffect()); + this.getSpellAbility().addTarget(new TargetOpponent()); } public RecurringInsight(final RecurringInsight card) { @@ -81,15 +81,13 @@ class RecurringInsightEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - TargetOpponent target = new TargetOpponent(); - Player you = game.getPlayer(source.getControllerId()); - if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) { - you.chooseTarget(Outcome.DrawCard, target, source, game); - Player opponent = game.getPlayer(target.getFirstTarget()); + Player controller = game.getPlayer(source.getControllerId()); + if (controller != null) { + Player opponent = game.getPlayer(getTargetPointer().getFirst(game, source)); if (opponent != null) { - you.drawCards(opponent.getHand().size(), game); - return true; + controller.drawCards(opponent.getHand().size(), game); } + return true; } return false; } From 5c6629919f343d490e8518fd61b5fb8cd45b8eb9 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 11 Jun 2015 00:31:17 +0200 Subject: [PATCH 092/102] * Nemesis of Reason - Fixed IndexOutOfBoundsException. --- .../sets/alarareborn/NemesisOfReason.java | 24 +++++++++++++------ .../mage/sets/alarareborn/Thraximundar.java | 8 +++++-- .../PutLibraryIntoGraveTargetEffect.java | 3 +++ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/Mage.Sets/src/mage/sets/alarareborn/NemesisOfReason.java b/Mage.Sets/src/mage/sets/alarareborn/NemesisOfReason.java index ffd7501ad19..276295c2860 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/NemesisOfReason.java +++ b/Mage.Sets/src/mage/sets/alarareborn/NemesisOfReason.java @@ -53,11 +53,14 @@ public class NemesisOfReason extends CardImpl { this.expansionSetCode = "ARB"; this.subtype.add("Leviathan"); this.subtype.add("Horror"); - this.power = new MageInt(3); this.toughness = new MageInt(7); - this.addAbility(new NemesisOfReasonTriggeredAbility()); + + // Whenever Nemesis of Reason attacks, defending player puts the top ten cards of his or her library into his or her graveyard. + Effect effect = new PutLibraryIntoGraveTargetEffect(10); + effect.setText("defending player puts the top ten cards of his or her library into his or her graveyard"); + this.addAbility(new NemesisOfReasonTriggeredAbility(effect)); } public NemesisOfReason (final NemesisOfReason card) { @@ -71,8 +74,9 @@ public class NemesisOfReason extends CardImpl { } class NemesisOfReasonTriggeredAbility extends TriggeredAbilityImpl { - NemesisOfReasonTriggeredAbility() { - super(Zone.BATTLEFIELD, new PutLibraryIntoGraveTargetEffect(10)); + + NemesisOfReasonTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect); } NemesisOfReasonTriggeredAbility(final NemesisOfReasonTriggeredAbility ability) { @@ -84,11 +88,17 @@ class NemesisOfReasonTriggeredAbility extends TriggeredAbilityImpl { return new NemesisOfReasonTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ATTACKER_DECLARED; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED && event.getSourceId().equals(this.getSourceId()) ) { - for (Effect effect : this.getEffects()) { - effect.setTargetPointer(new FixedTarget(event.getTargetId())); + if (event.getSourceId().equals(this.getSourceId()) ) { + UUID defenderId = game.getCombat().getDefendingPlayerId(this.getSourceId(), game); + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(defenderId)); } return true; } diff --git a/Mage.Sets/src/mage/sets/alarareborn/Thraximundar.java b/Mage.Sets/src/mage/sets/alarareborn/Thraximundar.java index ac23550a8a3..3028eded661 100644 --- a/Mage.Sets/src/mage/sets/alarareborn/Thraximundar.java +++ b/Mage.Sets/src/mage/sets/alarareborn/Thraximundar.java @@ -108,10 +108,14 @@ class ThraximundarTriggeredAbility extends TriggeredAbilityImpl { return new ThraximundarTriggeredAbility(this); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.ATTACKER_DECLARED; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.ATTACKER_DECLARED - && event.getSourceId() == this.getSourceId()) { + if (event.getSourceId() == this.getSourceId()) { UUID defender = game.getCombat().getDefendingPlayerId(this.getSourceId(), game); this.getEffects().get(0).setTargetPointer(new FixedTarget(defender)); return true; diff --git a/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java b/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java index d81b6a574cd..2efd33a0420 100644 --- a/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/PutLibraryIntoGraveTargetEffect.java @@ -82,6 +82,9 @@ public class PutLibraryIntoGraveTargetEffect extends OneShotEffect { @Override public String getText(Mode mode) { + if (staticText != null && !staticText.isEmpty()) { + return staticText; + } StringBuilder sb = new StringBuilder(); String message = amount.getMessage(); From 65401f1cbc96668afefd4084c0164ef8dce2ed48 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 11 Jun 2015 08:36:21 +0200 Subject: [PATCH 093/102] * Tiny Leaders - Fixed wrong split cards converted mana costs check. Added check for sideboard card that was missing. --- .../src/mage/deck/TinyLeaders.java | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java index 45e68e207a2..7144e68b5d8 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/src/mage/deck/TinyLeaders.java @@ -34,6 +34,7 @@ import java.util.List; import java.util.Map; import mage.abilities.common.CanBeYourCommanderAbility; import mage.cards.Card; +import mage.cards.SplitCard; import mage.cards.decks.Deck; import mage.cards.decks.DeckValidator; import mage.constants.CardType; @@ -170,15 +171,13 @@ public class TinyLeaders extends DeckValidator { if (!bannedCommander.contains(commander.getName())) { FilterMana color = CardUtil.getColorIdentity(commander); for (Card card : deck.getCards()) { - if (!cardHasValideColor(color, card)) { - invalid.put(card.getName(), "Invalid color (" + commander.getName() + ")"); - valid = false; + if (!isCardFormatValid(card, commander, color)) { + valid = false; } - - //905.5b - Converted mana cost must be 3 or less - if (card.getManaCost().convertedManaCost() > 3) { - invalid.put(card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ")"); - valid = false; + } + for (Card card : deck.getSideboard()) { + if (!isCardFormatValid(card, commander, color)) { + valid = false; } } } else { @@ -197,6 +196,31 @@ public class TinyLeaders extends DeckValidator { return valid; } + private boolean isCardFormatValid(Card card, Card commander, FilterMana color) { + if (!cardHasValideColor(color, card)) { + invalid.put(card.getName(), "Invalid color (" + commander.getName() + ")"); + return false; + } + + //905.5b - Converted mana cost must be 3 or less + if (card instanceof SplitCard) { + if (((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() > 3) { + invalid.put(card.getName(), "Invalid cost (" + ((SplitCard) card).getLeftHalfCard().getManaCost().convertedManaCost() + ")"); + return false; + } + if (((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() > 3 ) { + invalid.put(card.getName(), "Invalid cost (" + ((SplitCard) card).getRightHalfCard().getManaCost().convertedManaCost() + ")"); + return false; + } + } else { + if (card.getManaCost().convertedManaCost() > 3) { + invalid.put(card.getName(), "Invalid cost (" + card.getManaCost().convertedManaCost() + ")"); + return false; + } + } + return true; + } + /** * * @param commander FilterMana object with Color Identity of Commander set From ab4ed4f97317401760787f9ba6f825cd932b7442 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 11 Jun 2015 12:54:23 +0200 Subject: [PATCH 094/102] * Fixed a bug that cards with added abilities of continuous effects were not found always. --- .../predicate/mageobject/AbilityPredicate.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Mage/src/mage/filter/predicate/mageobject/AbilityPredicate.java b/Mage/src/mage/filter/predicate/mageobject/AbilityPredicate.java index 1deeefdd75a..42489015ab4 100644 --- a/Mage/src/mage/filter/predicate/mageobject/AbilityPredicate.java +++ b/Mage/src/mage/filter/predicate/mageobject/AbilityPredicate.java @@ -30,6 +30,7 @@ package mage.filter.predicate.mageobject; import mage.MageObject; import mage.abilities.Abilities; import mage.abilities.Ability; +import mage.cards.Card; import mage.filter.predicate.Predicate; import mage.game.Game; @@ -47,9 +48,15 @@ public class AbilityPredicate implements Predicate { @Override public boolean apply(MageObject input, Game game) { - Abilities abilities = input.getAbilities(); - for (int i = 0; i < abilities.size(); i++) { - if (abilityClass.equals(abilities.get(i).getClass())) { + Abilities abilities; + if (input instanceof Card){ + abilities = ((Card)input).getAbilities(game); + } else { + abilities = input.getAbilities(); + } + + for (Ability ability : abilities) { + if (abilityClass.equals(ability.getClass())) { return true; } } From 753340efc6d469ad5cdd40f1b96a9633e4c74745 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 11 Jun 2015 12:57:00 +0200 Subject: [PATCH 095/102] * Teferi, Mage of Zhalfir - Fixed that Flash was not given to cards in the libraray (so e.g. Mystical Teachings did not work for creatures that did not have Flash by itself). --- .../journeyintonyx/KeranosGodOfStorms.java | 2 - .../sets/timespiral/TeferiMageOfZhalfir.java | 7 +- .../add/AddAbilitiesToNonPermanentsTest.java | 78 +++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/abilities/add/AddAbilitiesToNonPermanentsTest.java diff --git a/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java b/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java index 3175e4c5d0b..f94df13e691 100644 --- a/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java +++ b/Mage.Sets/src/mage/sets/journeyintonyx/KeranosGodOfStorms.java @@ -46,7 +46,6 @@ import mage.cards.CardImpl; import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.ColoredManaSymbol; -import mage.constants.PhaseStep; import mage.constants.Rarity; import mage.constants.WatcherScope; import mage.constants.Zone; @@ -56,7 +55,6 @@ import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.common.TargetCreatureOrPlayer; import mage.watchers.Watcher; -import mage.watchers.common.CardsDrawnDuringDrawStepWatcher; /** * diff --git a/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java b/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java index e545ced4af1..12ce53027ff 100644 --- a/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java +++ b/Mage.Sets/src/mage/sets/timespiral/TeferiMageOfZhalfir.java @@ -126,7 +126,12 @@ class TeferiMageOfZhalfirAddFlashEffect extends ContinuousEffectImpl { game.getState().addOtherAbility(card, FlashAbility.getInstance()); } } - // in Library seems not relevant yet + // in Library (e.g. for Mystical Teachings) + for (Card card: controller.getLibrary().getCards(game)) { + if (card.getOwnerId().equals(controller.getId()) && card.getCardType().contains(CardType.CREATURE)) { + game.getState().addOtherAbility(card, FlashAbility.getInstance()); + } + } return true; } return false; diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/add/AddAbilitiesToNonPermanentsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/add/AddAbilitiesToNonPermanentsTest.java new file mode 100644 index 00000000000..242f9e5c2a2 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/add/AddAbilitiesToNonPermanentsTest.java @@ -0,0 +1,78 @@ +/* + * 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.cards.abilities.add; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.counters.CounterType; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class AddAbilitiesToNonPermanentsTest extends CardTestPlayerBase { + + + /** + * With Teferi, Mage of Zhalfir on the battlefield it has to be possible to search for + * a God of Storms in the deck by using Mystical Teachings. + * + */ + @Test + public void testSearchForCardWithFlashInLibrary() { + addCard(Zone.BATTLEFIELD, playerA, "Island", 4); + // Flash + // Creature cards you own that aren't on the battlefield have flash. + // Each opponent can cast spells only any time he or she could cast a sorcery. + addCard(Zone.BATTLEFIELD, playerA, "Teferi, Mage of Zhalfir"); + + // Search your library for an instant card or a card with flash, reveal it, and put it into your hand. Then shuffle your library. + // Flashback {5}{B} + addCard(Zone.HAND, playerA, "Mystical Teachings"); // "{3}{U}" + + addCard(Zone.LIBRARY, playerA, "Keranos, God of Storms"); + addCard(Zone.LIBRARY, playerA, "Plains", 3); + + skipInitShuffling(); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mystical Teachings"); + addTarget(playerA, "Keranos, God of Storms"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Mystical Teachings", 1); + assertHandCount(playerA, "Keranos, God of Storms", 1); + } +} From d713ae424ff1dc4b3c1b1e9b91478fdbb1d27ae1 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 11 Jun 2015 15:52:47 +0200 Subject: [PATCH 096/102] * Enduring Renewal - Fixed that the creature did not come back to hand. --- .../mage/sets/fifthdawn/GrindingStation.java | 2 +- .../triggers/ReturnToHandEffectsTest.java | 72 +++++++++++++++++++ ...aveFromBattlefieldAllTriggeredAbility.java | 2 + 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToHandEffectsTest.java diff --git a/Mage.Sets/src/mage/sets/fifthdawn/GrindingStation.java b/Mage.Sets/src/mage/sets/fifthdawn/GrindingStation.java index de1b9388df7..5b36e9d7db7 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/GrindingStation.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/GrindingStation.java @@ -61,7 +61,7 @@ public class GrindingStation extends CardImpl { super(ownerId, 127, "Grinding Station", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{2}"); this.expansionSetCode = "5DN"; - // {tap}, Sacrifice an artifact: Target player puts the top three cards of his or her library into his or her graveyard. + // {T}, Sacrifice an artifact: Target player puts the top three cards of his or her library into his or her graveyard. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutTopCardOfLibraryIntoGraveTargetEffect(3), new TapSourceCost()); ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(filter))); ability.addTarget(new TargetPlayer()); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToHandEffectsTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToHandEffectsTest.java new file mode 100644 index 00000000000..9159d14fd9d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/triggers/ReturnToHandEffectsTest.java @@ -0,0 +1,72 @@ +/* + * 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.cards.triggers; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class ReturnToHandEffectsTest extends CardTestPlayerBase { + + /** + * Enduring Renewal doesn't return creatures to hand put into graveyard from + * the battlefield It happened with Enduring Renewal in the battlefield + * while feeding Ornithopter to Grinding Station + */ + @Test + public void testEnduringRenewal() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2); + // Play with your hand revealed. + // If you would draw a card, reveal the top card of your library instead. If it's a creature card, put it into your graveyard. Otherwise, draw a card. + // Whenever a creature is put into your graveyard from the battlefield, return it to your hand. + addCard(Zone.BATTLEFIELD, playerA, "Enduring Renewal"); + + // {T}, Sacrifice an artifact: Target player puts the top three cards of his or her library into his or her graveyard. + // Whenever an artifact enters the battlefield, you may untap Grinding Station. + addCard(Zone.BATTLEFIELD, playerA, "Grinding Station", 1); + addCard(Zone.BATTLEFIELD, playerA, "Ornithopter", 1); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice an artifact", playerB); + // addTarget(playerA, "Ornithopter"); + setChoice(playerA, "Ornithopter"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertGraveyardCount(playerB, 3); + assertHandCount(playerA, "Ornithopter", 1); + + } + +} diff --git a/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java b/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java index be6495f8c90..45b41aec3ef 100644 --- a/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/PutIntoGraveFromBattlefieldAllTriggeredAbility.java @@ -54,12 +54,14 @@ public class PutIntoGraveFromBattlefieldAllTriggeredAbility extends TriggeredAbi super(Zone.BATTLEFIELD, effect, optional); this.filter = filter; this.onlyToControllerGraveyard = onlyToControllerGraveyard; + this.setTargetPointer = setTargetPointer; } public PutIntoGraveFromBattlefieldAllTriggeredAbility(final PutIntoGraveFromBattlefieldAllTriggeredAbility ability) { super(ability); this.filter = ability.filter; this.onlyToControllerGraveyard = ability.onlyToControllerGraveyard; + this.setTargetPointer = ability.setTargetPointer; } @Override From f7bce827fed9fa7bd54815d1d072dc14df76cc19 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Thu, 11 Jun 2015 17:14:43 +0200 Subject: [PATCH 097/102] * Refraction Trap - Fixed a bunch of problems with that card. --- .../mage/sets/worldwake/RefractionTrap.java | 106 +++++++++--------- .../prevent/RefractionTrapTest.java | 78 +++++++++++++ 2 files changed, 134 insertions(+), 50 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/RefractionTrapTest.java diff --git a/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java b/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java index 27641883e14..de636931bd9 100644 --- a/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java +++ b/Mage.Sets/src/mage/sets/worldwake/RefractionTrap.java @@ -27,6 +27,8 @@ */ package mage.sets.worldwake; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import mage.constants.CardType; @@ -34,16 +36,19 @@ import mage.constants.Rarity; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.costs.AlternativeCostImpl; -import mage.abilities.costs.mana.ColoredManaCost; +import mage.abilities.costs.mana.ManaCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.PreventionEffectData; import mage.abilities.effects.PreventionEffectImpl; import mage.cards.CardImpl; -import mage.constants.ColoredManaSymbol; import mage.constants.Duration; +import mage.constants.Outcome; import mage.constants.WatcherScope; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.Spell; +import mage.game.stack.StackObject; import mage.players.Player; import mage.target.TargetSource; import mage.target.common.TargetCreatureOrPlayer; @@ -66,7 +71,6 @@ public class RefractionTrap extends CardImpl { // Prevent the next 3 damage that a source of your choice would deal to you and/or permanents you control this turn. If damage is prevented this way, Refraction Trap deals that much damage to target creature or player. this.getSpellAbility().addEffect(new RefractionTrapPreventDamageEffect(Duration.EndOfTurn, 3)); - this.getSpellAbility().addTarget(new TargetSource()); this.getSpellAbility().addTarget(new TargetCreatureOrPlayer()); this.getSpellAbility().addWatcher(new RefractionTrapWatcher()); @@ -84,12 +88,15 @@ public class RefractionTrap extends CardImpl { class RefractionTrapWatcher extends Watcher { + Set playersMetCondition = new HashSet<>(); + public RefractionTrapWatcher() { super("RefractionTrapWatcher", WatcherScope.GAME); } public RefractionTrapWatcher(final RefractionTrapWatcher watcher) { super(watcher); + this.playersMetCondition.addAll(watcher.playersMetCondition); } @Override @@ -99,26 +106,33 @@ class RefractionTrapWatcher extends Watcher { @Override public void watch(GameEvent event, Game game) { - if (condition == true) //no need to check - condition has already occured - { - return; - } - if (event.getType() == GameEvent.EventType.SPELL_CAST - && game.getOpponents(controllerId).contains(event.getPlayerId())) { + if (event.getType() == GameEvent.EventType.SPELL_CAST) { Spell spell = game.getStack().getSpell(event.getTargetId()); if (spell.getColor(game).isRed()) { if (spell.getCardType().contains(CardType.INSTANT) || spell.getCardType().contains(CardType.SORCERY)) { - condition = true; + playersMetCondition.add(event.getPlayerId()); } } } } + public boolean conditionMetForAnOpponent(UUID controllerId, Game game) { + Player controller = game.getPlayer(controllerId); + if (controller != null) { + for(UUID playerId: playersMetCondition) { + if (controller.hasOpponent(playerId, game)) { + return true; + } + } + } + return false; + + } @Override public void reset() { - super.reset(); - condition = false; + playersMetCondition.clear(); + super.reset(); } } @@ -126,7 +140,7 @@ class RefractionTrapAlternativeCost extends AlternativeCostImpl { public RefractionTrapAlternativeCost() { super("You may pay {W} rather than pay Refraction Trap's mana cost"); - this.add(new ColoredManaCost(ColoredManaSymbol.W)); + this.add(new ManaCostsImpl("{W}")); } public RefractionTrapAlternativeCost(final RefractionTrapAlternativeCost cost) { @@ -141,10 +155,7 @@ class RefractionTrapAlternativeCost extends AlternativeCostImpl { @Override public boolean isAvailable(Game game, Ability source) { RefractionTrapWatcher watcher = (RefractionTrapWatcher) game.getState().getWatchers().get("RefractionTrapWatcher"); - if (watcher != null && watcher.conditionMet()) { - return true; - } - return false; + return watcher != null && watcher.conditionMetForAnOpponent(source.getControllerId(), game); } @Override @@ -155,24 +166,33 @@ class RefractionTrapAlternativeCost extends AlternativeCostImpl { class RefractionTrapPreventDamageEffect extends PreventionEffectImpl { + private final TargetSource target; private int amount; public RefractionTrapPreventDamageEffect(Duration duration, int amount) { - super(duration); + super(duration, amount, false, false); this.amount = amount; + this.target = new TargetSource(); staticText = "The next " + amount + " damage that a source of your choice would deal to you and/or permanents you control this turn. If damage is prevented this way, {this} deals that much damage to target creature or player"; } public RefractionTrapPreventDamageEffect(final RefractionTrapPreventDamageEffect effect) { super(effect); this.amount = effect.amount; + this.target = effect.target.copy(); } @Override public RefractionTrapPreventDamageEffect copy() { return new RefractionTrapPreventDamageEffect(this); } - + + @Override + public void init(Ability source, Game game) { + this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game); + super.init(source, game); + } + @Override public boolean apply(Game game, Ability source) { return true; @@ -180,45 +200,29 @@ class RefractionTrapPreventDamageEffect extends PreventionEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - GameEvent preventEvent = new GameEvent(GameEvent.EventType.PREVENT_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), event.getAmount(), false); - if (!game.replaceEvent(preventEvent)) { - int prevented = 0; - if (event.getAmount() >= this.amount) { - int damage = amount; - event.setAmount(event.getAmount() - amount); - this.used = true; - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), damage)); - prevented = damage; - } else { - int damage = event.getAmount(); - event.setAmount(0); - amount -= damage; - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.PREVENTED_DAMAGE, source.getFirstTarget(), source.getSourceId(), source.getControllerId(), damage)); - prevented = damage; + PreventionEffectData preventionData = preventDamageAction(event, source, game); + this.used = true; + this.discard(); // only one use + if (preventionData.getPreventedDamage() > 0) { + UUID damageTarget = getTargetPointer().getFirst(game, source); + Permanent permanent = game.getPermanent(damageTarget); + if (permanent != null) { + game.informPlayers("Dealing " + preventionData.getPreventedDamage() + " to " + permanent.getLogName()); + permanent.damage(preventionData.getPreventedDamage(), source.getSourceId(), game, false, true); } - - // deal damage now - if (prevented > 0) { - UUID damageTarget = source.getTargets().get(1).getFirstTarget(); - Permanent target = game.getPermanent(damageTarget); - if (target != null) { - game.informPlayers("Dealing " + prevented + " to " + target.getName()); - target.damage(prevented, source.getSourceId(), game, false, true); - } - Player player = game.getPlayer(damageTarget); - if (player != null) { - game.informPlayers("Dealing " + prevented + " to " + player.getLogName()); - player.damage(prevented, source.getSourceId(), game, true, false); - } + Player player = game.getPlayer(damageTarget); + if (player != null) { + game.informPlayers("Dealing " + preventionData.getPreventedDamage() + " to " + player.getLogName()); + player.damage(preventionData.getPreventedDamage(), source.getSourceId(), game, true, false); } } + return false; } @Override public boolean applies(GameEvent event, Ability source, Game game) { if (!this.used && super.applies(event, source, game)) { - // check source MageObject object = game.getObject(event.getSourceId()); if (object == null) { @@ -226,7 +230,9 @@ class RefractionTrapPreventDamageEffect extends PreventionEffectImpl { return false; } - if (!object.getId().equals(source.getFirstTarget())) { + // check damage source + if (!object.getId().equals(target.getFirstTarget()) && + !((object instanceof StackObject) && ((StackObject)object).getSourceId().equals(target.getFirstTarget()))) { return false; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/RefractionTrapTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/RefractionTrapTest.java new file mode 100644 index 00000000000..4b013c46cad --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/replacement/prevent/RefractionTrapTest.java @@ -0,0 +1,78 @@ +/* + * 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.cards.replacement.prevent; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ + +public class RefractionTrapTest extends CardTestPlayerBase { + + /** + * I found a bug when i cast Refraction Trap I choose to redirect damage to + * opponent creature and it stays alive (in 2 situations) + */ + @Test + public void testPreventDamageFromSpell() { + addCard(Zone.HAND, playerA, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerA, "Mountain"); + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion"); + + // If an opponent cast a red instant or sorcery spell this turn, you may pay {W} + // rather than pay Refraction Trap's mana cost. + // Prevent the next 3 damage that a source of your choice would deal to you and/or + // permanents you control this turn. If damage is prevented this way, Refraction Trap + // deals that much damage to target creature or player. + addCard(Zone.HAND, playerB, "Refraction Trap"); + addCard(Zone.BATTLEFIELD, playerB, "Plains"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Refraction Trap", "Silvercoat Lion", "Lightning Bolt"); + setChoice(playerB, "Yes"); + setChoice(playerB, "Lightning Bolt"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + + assertLife(playerA, 20); + assertLife(playerB, 20); + + assertGraveyardCount(playerA, "Lightning Bolt", 1); + assertGraveyardCount(playerB, "Refraction Trap", 1); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + + } + +} \ No newline at end of file From 0d0cc343090b50b483961aa2cf9bfba8b433fe71 Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Tue, 9 Jun 2015 23:00:18 -0400 Subject: [PATCH 098/102] Added Repercussion --- .../mage/sets/urzasdestiny/Repercussion.java | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java b/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java new file mode 100644 index 00000000000..2a623ffcf97 --- /dev/null +++ b/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java @@ -0,0 +1,140 @@ +/* + * 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 mage.sets.urzasdestiny; + +import java.util.UUID; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.DealtDamageToSourceTriggeredAbility; +import mage.abilities.effects.common.DamagePlayersEffect; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; +import mage.abilities.effects.OneShotEffect; + +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.players.Player; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author cbrianhill + */ +public class Repercussion extends CardImpl { + + public Repercussion(UUID ownerId) { + super(ownerId, 95, "Repercussion", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}{R}"); + this.expansionSetCode = "UDS"; + + // Whenever a creature is dealt damage, Repercussion deals that much damage to that creature's controller. + this.addAbility(new RepercussionTriggeredAbility(new RepercussionEffect())); + } + + public Repercussion(final Repercussion card) { + super(card); + } + + @Override + public Repercussion copy() { + return new Repercussion(this); + } +} + +class RepercussionTriggeredAbility extends TriggeredAbilityImpl { + + static final String PLAYER_DAMAGE_AMOUNT_KEY = "playerDamage"; + + private int count; + + public RepercussionTriggeredAbility(Effect effect) { + super(Zone.BATTLEFIELD, effect); + } + + public RepercussionTriggeredAbility(final RepercussionTriggeredAbility ability) { + super(ability); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) { + MageObject eventSource = game.getObject(event.getSourceId()); + this.getEffects().get(0).setValue(PLAYER_DAMAGE_AMOUNT_KEY, event.getAmount()); + this.getEffects().get(0).setTargetPointer(new FixedTarget(game.getControllerId(eventSource.getId()))); + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever a creature is dealt damage, {this} deals that much damage to that creature's controller."; + } + + @Override + public TriggeredAbility copy() { + return new RepercussionTriggeredAbility(this); + } +} + +class RepercussionEffect extends OneShotEffect { + + public RepercussionEffect() { + super(Outcome.Damage); + } + + public RepercussionEffect(final RepercussionEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Integer playerDamage = (Integer)this.getValue(RepercussionTriggeredAbility.PLAYER_DAMAGE_AMOUNT_KEY); + UUID targetId = this.targetPointer.getFirst(game, source); + if (playerDamage != null && targetId != null) { + Player player = game.getPlayer(targetId); + if (player != null) { + player.damage(playerDamage, targetId, game, false, true); + return true; + } + } + return false; + } + + @Override + public Effect copy() { + return new RepercussionEffect(this); + } + +} From 81c4ae5d84fd88fb90dc058375a25ea0810cff23 Mon Sep 17 00:00:00 2001 From: Brian Hill Date: Wed, 10 Jun 2015 22:37:28 -0400 Subject: [PATCH 099/102] Incorporate feedback from LevelX2 --- .../mage/sets/urzasdestiny/Repercussion.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java b/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java index 2a623ffcf97..9140b797f53 100644 --- a/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java +++ b/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java @@ -86,15 +86,17 @@ class RepercussionTriggeredAbility extends TriggeredAbilityImpl { super(ability); } + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.DAMAGED_CREATURE; + } + @Override public boolean checkTrigger(GameEvent event, Game game) { - if (event.getType() == GameEvent.EventType.DAMAGED_CREATURE) { - MageObject eventSource = game.getObject(event.getSourceId()); - this.getEffects().get(0).setValue(PLAYER_DAMAGE_AMOUNT_KEY, event.getAmount()); - this.getEffects().get(0).setTargetPointer(new FixedTarget(game.getControllerId(eventSource.getId()))); - return true; - } - return false; + MageObject eventSource = game.getObject(event.getSourceId()); + this.getEffects().get(0).setValue(PLAYER_DAMAGE_AMOUNT_KEY, event.getAmount()); + this.getEffects().get(0).setTargetPointer(new FixedTarget(game.getControllerId(eventSource.getId()))); + return true; } @Override @@ -125,7 +127,7 @@ class RepercussionEffect extends OneShotEffect { if (playerDamage != null && targetId != null) { Player player = game.getPlayer(targetId); if (player != null) { - player.damage(playerDamage, targetId, game, false, true); + player.damage(playerDamage, source.getSourceId(), game, false, true); return true; } } From 4f6d3f3a1accee75a3cac6ca0e8900347b099ce2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 12 Jun 2015 08:12:38 +0200 Subject: [PATCH 100/102] * Password join value is now distinct from password you set of for a new table. --- .../src/main/java/mage/client/dialog/JoinTableDialog.java | 4 ++-- .../src/main/java/mage/client/dialog/PreferencesDialog.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java index 5413b8fc176..706fe30e784 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java @@ -51,7 +51,7 @@ public class JoinTableDialog extends MageDialog { public JoinTableDialog() { initComponents(); newPlayerPanel.showLevel(false); - txtPassword.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD, "")); + txtPassword.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD_JOIN, "")); } public void showDialog(UUID roomId, UUID tableId, boolean isTournament, boolean isLimited) { @@ -148,7 +148,7 @@ public class JoinTableDialog extends MageDialog { private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed Session session = MageFrame.getSession(); try { - PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD, txtPassword.getText()); + PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD_JOIN, txtPassword.getText()); if (isTournament) { joined = session.joinTournamentTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), "Human", 1, DeckImporterUtil.importDeck(this.newPlayerPanel.getDeckFile()), this.txtPassword.getText()); } else { diff --git a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java index 75d8bf82b62..a55dee9d986 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PreferencesDialog.java @@ -164,6 +164,7 @@ public class PreferencesDialog extends javax.swing.JDialog { // pref setting for new table dialog public static final String KEY_NEW_TABLE_NAME = "newTableName"; public static final String KEY_NEW_TABLE_PASSWORD = "newTablePassword"; + public static final String KEY_NEW_TABLE_PASSWORD_JOIN = "newTablePasswordJoin"; public static final String KEY_NEW_TABLE_DECK_TYPE = "newTableDeckType"; public static final String KEY_NEW_TABLE_TIME_LIMIT = "newTableTimeLimit"; public static final String KEY_NEW_TABLE_GAME_TYPE = "newTableGameType"; From 7f5e3a262e281d9c98e81555d119ff083c7fe03c Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 12 Jun 2015 08:13:41 +0200 Subject: [PATCH 101/102] * Added test and some minor changes. --- .../mage/sets/magic2012/PhantasmalImage.java | 4 +- .../sets/scarsofmirrodin/WurmcoilEngine.java | 4 +- .../src/mage/sets/tenthedition/Clone.java | 1 + .../src/mage/sets/tenthedition/Nightmare.java | 4 ++ .../org/mage/test/cards/copy/CloneTest.java | 2 + .../test/cards/copy/PhantasmalImageTest.java | 45 +++++++++++++++++++ 6 files changed, 58 insertions(+), 2 deletions(-) diff --git a/Mage.Sets/src/mage/sets/magic2012/PhantasmalImage.java b/Mage.Sets/src/mage/sets/magic2012/PhantasmalImage.java index 576bfd7005b..3fef0ff88fb 100644 --- a/Mage.Sets/src/mage/sets/magic2012/PhantasmalImage.java +++ b/Mage.Sets/src/mage/sets/magic2012/PhantasmalImage.java @@ -110,7 +110,9 @@ class PhantasmalImageCopyEffect extends OneShotEffect { if (!permanent.getSubtype().contains("Illusion")) { permanent.getSubtype().add("Illusion"); } - permanent.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), game); + // Add directly because the created permanent is only used to copy from, so there is no need to add the ability to e.g. TriggeredAbilities + permanent.getAbilities().add(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect())); + //permanent.addAbility(new BecomesTargetTriggeredAbility(new SacrificeSourceEffect()), game); return true; } }); diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java index 6bdf90e9803..2a911f2e7f0 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java @@ -54,9 +54,11 @@ public class WurmcoilEngine extends CardImpl { this.power = new MageInt(6); this.toughness = new MageInt(6); - + // Deathtouch, lifelink this.addAbility(DeathtouchAbility.getInstance()); this.addAbility(LifelinkAbility.getInstance()); + + // When Wurmcoil Engine dies, put a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink onto the battlefield. Ability ability = new DiesTriggeredAbility(new CreateTokenEffect(new Wurm1Token(expansionSetCode)), false); ability.addEffect(new CreateTokenEffect(new Wurm2Token(expansionSetCode))); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/tenthedition/Clone.java b/Mage.Sets/src/mage/sets/tenthedition/Clone.java index 2284d16afac..2412dacd079 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/Clone.java +++ b/Mage.Sets/src/mage/sets/tenthedition/Clone.java @@ -53,6 +53,7 @@ public class Clone extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(0); + // You may have Clone enter the battlefield as a copy of any creature on the battlefield. Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new EntersBattlefieldEffect( new CopyPermanentEffect(), "You may have {this} enter the battlefield as a copy of any creature on the battlefield", diff --git a/Mage.Sets/src/mage/sets/tenthedition/Nightmare.java b/Mage.Sets/src/mage/sets/tenthedition/Nightmare.java index 42e0b57e4f9..57a192538c7 100644 --- a/Mage.Sets/src/mage/sets/tenthedition/Nightmare.java +++ b/Mage.Sets/src/mage/sets/tenthedition/Nightmare.java @@ -61,7 +61,11 @@ public class Nightmare extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(0); + + // Flying this.addAbility(FlyingAbility.getInstance()); + + // Nightmare's power and toughness are each equal to the number of Swamps you control. this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerToughnessSourceEffect(new PermanentsOnBattlefieldCount(filter), Duration.EndOfGame))); } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java index 47bf48d5091..b02bbe4e383 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/CloneTest.java @@ -108,9 +108,11 @@ public class CloneTest extends CardTestPlayerBase { addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1); addCard(Zone.BATTLEFIELD, playerB, "Swamp", 6); addCard(Zone.BATTLEFIELD, playerB, "Forest", 1); + // Target creature you control gets +1/+1 and gains hexproof until end of turn. (It can't be the target of spells or abilities your opponents control.) addCard(Zone.HAND, playerB, "Ranger's Guile"); addCard(Zone.HAND, playerA, "Clone"); + // Return target nonland permanent to its owner's hand. addCard(Zone.HAND, playerA, "Disperse"); addCard(Zone.BATTLEFIELD, playerB, "Nightmare", 1); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java index 7cd0d98e3b0..7b1ecd66b1e 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/copy/PhantasmalImageTest.java @@ -525,5 +525,50 @@ public class PhantasmalImageTest extends CardTestPlayerBase { assertPermanentCount(playerB, "Butcher Ghoul", 1); assertPowerToughness(playerB, "Butcher Ghoul", 2, 2); + } + + /** + * 12:29: Attacker: Wurmcoil Engine [466] (6/6) blocked by Wurmcoil Engine + * [4ed] (6/6) + * 12:29: yespair gains 6 life + * 12:29: HipSomHap gains 6 life + * 12:29: Wurmcoil Engine [4ed] died + * 12:29: Ability triggers: Wurmcoil Engine [4ed] - When Wurmcoil Engine [4ed] dies, put a a 3/3 colorless + * Wurm artifact creature token with deathtouch onto the battlefield. Put a + * a 3/3 colorless Wurm artifact creature token with lifelink onto the + * battlefield. + * 12:29: Phantasmal Image [466] died + * 12:29: HipSomHap puts a Wurm [7d0] token onto the battlefield + * 12:29: HipSomHap puts a Wurm [186] token onto the battlefield + * + * To the best of my knowledge, the Phantasmal Image [466], which entered + * the battlefield as a Wurmcoil Engine, should grant tokens through the + * Dies-trigger as well, right? + */ + + @Test + public void testDiesTriggered2() { + addCard(Zone.BATTLEFIELD, playerB, "Wurmcoil Engine"); + + addCard(Zone.BATTLEFIELD, playerA, "Island", 2); + addCard(Zone.HAND, playerA, "Phantasmal Image"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phantasmal Image"); // not targeted + setChoice(playerB, "Wurmcoil Engine"); + + attack(2, playerB, "Wurmcoil Engine"); + block(2, playerA, "Wurmcoil Engine", "Wurmcoil Engine"); + setStopAt(2, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerB, 26); + assertLife(playerA, 26); + + assertGraveyardCount(playerA, "Phantasmal Image", 1); + assertGraveyardCount(playerB, "Wurmcoil Engine", 1); + + assertPermanentCount(playerA, "Wurm", 2); + assertPermanentCount(playerB, "Wurm", 2); + } } From 8acfbf0ab32be2d7a7e378daa516947af74a57d8 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Fri, 12 Jun 2015 13:51:20 +0200 Subject: [PATCH 102/102] * Repercussion - Some updates. --- .../mage/sets/urzasdestiny/Repercussion.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java b/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java index 9140b797f53..a1018936db2 100644 --- a/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java +++ b/Mage.Sets/src/mage/sets/urzasdestiny/Repercussion.java @@ -28,13 +28,10 @@ package mage.sets.urzasdestiny; import java.util.UUID; -import mage.MageObject; +import mage.MageObjectReference; import mage.abilities.Ability; import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.common.DealtDamageToSourceTriggeredAbility; -import mage.abilities.effects.common.DamagePlayersEffect; -import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -45,8 +42,8 @@ import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; import mage.players.Player; -import mage.target.targetpointer.FixedTarget; /** * @@ -75,8 +72,7 @@ public class Repercussion extends CardImpl { class RepercussionTriggeredAbility extends TriggeredAbilityImpl { static final String PLAYER_DAMAGE_AMOUNT_KEY = "playerDamage"; - - private int count; + static final String TRIGGERING_CREATURE_KEY = "triggeringCreature"; public RepercussionTriggeredAbility(Effect effect) { super(Zone.BATTLEFIELD, effect); @@ -93,9 +89,10 @@ class RepercussionTriggeredAbility extends TriggeredAbilityImpl { @Override public boolean checkTrigger(GameEvent event, Game game) { - MageObject eventSource = game.getObject(event.getSourceId()); - this.getEffects().get(0).setValue(PLAYER_DAMAGE_AMOUNT_KEY, event.getAmount()); - this.getEffects().get(0).setTargetPointer(new FixedTarget(game.getControllerId(eventSource.getId()))); + for(Effect effect : getEffects()) { + effect.setValue(PLAYER_DAMAGE_AMOUNT_KEY, event.getAmount()); + effect.setValue(TRIGGERING_CREATURE_KEY, new MageObjectReference(event.getTargetId(), game)); + } return true; } @@ -123,13 +120,16 @@ class RepercussionEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Integer playerDamage = (Integer)this.getValue(RepercussionTriggeredAbility.PLAYER_DAMAGE_AMOUNT_KEY); - UUID targetId = this.targetPointer.getFirst(game, source); - if (playerDamage != null && targetId != null) { - Player player = game.getPlayer(targetId); - if (player != null) { + MageObjectReference mor = (MageObjectReference)this.getValue(RepercussionTriggeredAbility.TRIGGERING_CREATURE_KEY); + if (playerDamage != null && mor != null) { + Permanent creature = mor.getPermanentOrLKIBattlefield(game); + if (creature != null) { + Player player = game.getPlayer(creature.getControllerId()); + if (player != null) { player.damage(playerDamage, source.getSourceId(), game, false, true); - return true; + } } + return true; } return false; }