diff --git a/.gitignore b/.gitignore index e1956605db..0c1f55de65 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ Mage.Plugins/Mage.Card.Plugin/target Mage.Plugins/Mage.Counter.Plugin/target Mage.Plugins/Mage.Theme.Plugin/target Mage.Plugins/Mage.Rating.Plugin/target +Mage.Server.Console/target/ Mage.Server.Plugins/Mage.Deck.Constructed/target Mage.Server.Plugins/Mage.Deck.Limited/target Mage.Server.Plugins/Mage.Game.FreeForAll/target @@ -16,6 +17,7 @@ Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/target Mage.Server.Plugins/Mage.Player.AI/target Mage.Server.Plugins/Mage.Player.AIMinimax/target Mage.Server.Plugins/Mage.Player.AI.MA/target +Mage.Server.Plugins/Mage.Player.AIMCTS/target Mage.Server.Plugins/Mage.Player.Human/target Mage.Server.Plugins/Mage.Draft.8PlayerBooster/target Mage.Server.Plugins/Mage.Tournament.BoosterDraft/target @@ -24,6 +26,7 @@ Mage.Server/target Mage.Sets/target Mage.Tests/target Mage/target +Utils/GathererCrawler/target releases Utils/author.txt @@ -40,7 +43,6 @@ syntax: regexp nbactions.xml glob:Mage.Client/cheat.dck glob:Mage.Client/test.dck -glob:Mage.Server.Console/target/ Mage.Server.Plugins/Mage.Draft.8PlayerBooster/target \.orig\..*$ @@ -49,4 +51,4 @@ Mage.Server.Plugins/Mage.Draft.8PlayerBooster/target \.rej$ \.conflict\~$ /Mage.Server.Plugins/Mage.Player.AIMCTS/target/ -/Mage.Server.Console/target/ \ No newline at end of file +/Mage.Server.Console/target/ diff --git a/Mage.Client/release/sample-decks/World Championship 2011/Tommy Ashton – 12 points.dck b/Mage.Client/release/sample-decks/World Championship 2011/Tommy Ashton - 12 points.dck similarity index 100% rename from Mage.Client/release/sample-decks/World Championship 2011/Tommy Ashton – 12 points.dck rename to Mage.Client/release/sample-decks/World Championship 2011/Tommy Ashton - 12 points.dck diff --git a/Mage.Common/pom.xml b/Mage.Common/pom.xml index b54b291129..c6173ac3b1 100644 --- a/Mage.Common/pom.xml +++ b/Mage.Common/pom.xml @@ -50,6 +50,13 @@ trove 1.0.2 + + + org.sqlite + sqlite + 0.5.6 + + diff --git a/Mage.Common/src/mage/db/EntityManager.java b/Mage.Common/src/mage/db/EntityManager.java new file mode 100644 index 0000000000..567bdc6213 --- /dev/null +++ b/Mage.Common/src/mage/db/EntityManager.java @@ -0,0 +1,35 @@ +package mage.db; + +import java.sql.*; + +/** + * @author noxx + */ +public class EntityManager { + + public static void main(String[] args) throws Exception { + Class.forName("org.sqlite.JDBC"); + Connection conn = DriverManager.getConnection("jdbc:sqlite:mage.db"); + Statement stat = conn.createStatement(); + stat.executeUpdate("drop table if exists users;"); + stat.executeUpdate("create table users (login, password);"); + + PreparedStatement prep = conn.prepareStatement("insert into users values (?, ?);"); + + prep.setString(1, "TestUser"); + prep.setString(2, "123"); + prep.execute(); + + prep.setString(1, "TestUser2"); + prep.setString(2, "12345"); + prep.execute(); + + ResultSet rs = stat.executeQuery("select * from users;"); + while (rs.next()) { + System.out.println("user = " + rs.getString("login")); + System.out.println("password = " + rs.getString("password")); + } + rs.close(); + conn.close(); + } +} diff --git a/Mage.Common/src/mage/db/model/User.java b/Mage.Common/src/mage/db/model/User.java new file mode 100644 index 0000000000..2664aaad1f --- /dev/null +++ b/Mage.Common/src/mage/db/model/User.java @@ -0,0 +1,11 @@ +package mage.db.model; + +/** + * @author noxx + */ +public class User { + + private String login; + + private String password; +} diff --git a/Mage.Common/src/mage/remote/MageVersionException.java b/Mage.Common/src/mage/remote/MageVersionException.java index b098da6eb7..7b5b4185f2 100644 --- a/Mage.Common/src/mage/remote/MageVersionException.java +++ b/Mage.Common/src/mage/remote/MageVersionException.java @@ -39,7 +39,7 @@ public class MageVersionException extends MageException { private MageVersion serverVersion; public MageVersionException(MageVersion clientVersion, MageVersion serverVersion) { - super("Wrong client version " + clientVersion + ", expecting version " + serverVersion); + super("Wrong client version " + clientVersion + ", expecting version " + serverVersion + ". \r\n\r\nPlease download new version at magefree.com."); this.serverVersion = serverVersion; } diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index c442d8e4ea..c330fb5877 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -28,43 +28,41 @@ package mage.server; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.ExecutorService; import mage.MageException; import mage.cards.decks.DeckCardLists; import mage.game.GameException; import mage.game.match.MatchOptions; import mage.game.tournament.TournamentOptions; +import mage.interfaces.Action; import mage.interfaces.MageServer; -//import mage.interfaces.Server; import mage.interfaces.ServerState; import mage.remote.MageVersionException; -import mage.server.game.DeckValidatorFactory; import mage.server.draft.DraftManager; -import mage.server.game.GameFactory; -import mage.server.game.GameManager; -import mage.server.game.GamesRoomManager; -import mage.server.game.PlayerFactory; -import mage.server.game.ReplayManager; +import mage.server.game.*; import mage.server.tournament.TournamentFactory; import mage.server.tournament.TournamentManager; import mage.server.util.ServerMessagesUtil; import mage.server.util.ThreadExecutor; import mage.utils.CompressUtil; import mage.utils.MageVersion; -import mage.view.*; import mage.view.ChatMessage.MessageColor; +import mage.view.*; import org.apache.log4j.Logger; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ExecutorService; + +//import mage.interfaces.Server; + /** * * @author BetaSteward_at_googlemail.com */ public class MageServerImpl implements MageServer { - private final static Logger logger = Logger.getLogger("Mage Server"); + private final static Logger logger = Logger.getLogger(MageServerImpl.class); private static ExecutorService callExecutor = ThreadExecutor.getInstance().getCallExecutor(); private String password; @@ -79,8 +77,10 @@ public class MageServerImpl implements MageServer { @Override public boolean registerClient(String userName, String sessionId, MageVersion version) throws MageException { try { - if (version.compareTo(Main.getVersion()) != 0) + if (version.compareTo(Main.getVersion()) != 0) { + logger.info("MageVersionException: userName=" + userName + ", version=" + version); throw new MageVersionException(version, Main.getVersion()); + } return SessionManager.getInstance().registerUser(sessionId, userName); } catch (Exception ex) { if (ex instanceof MageVersionException) @@ -148,24 +148,12 @@ public class MageServerImpl implements MageServer { @Override public void removeTable(final String sessionId, final UUID roomId, final UUID tableId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - TableManager.getInstance().removeTable(userId, tableId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + TableManager.getInstance().removeTable(userId, tableId); + } + }); } @Override @@ -302,68 +290,32 @@ public class MageServerImpl implements MageServer { @Override public void startMatch(final String sessionId, final UUID roomId, final UUID tableId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); TableManager.getInstance().startMatch(userId, roomId, tableId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + } + }); } @Override public void startChallenge(final String sessionId, final UUID roomId, final UUID tableId, final UUID challengeId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - TableManager.getInstance().startChallenge(userId, roomId, tableId, challengeId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + TableManager.getInstance().startChallenge(userId, roomId, tableId, challengeId); + } + }); + } @Override public void startTournament(final String sessionId, final UUID roomId, final UUID tableId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - TableManager.getInstance().startTournament(userId, roomId, tableId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + TableManager.getInstance().startTournament(userId, roomId, tableId); + } + }); } @Override @@ -396,42 +348,22 @@ public class MageServerImpl implements MageServer { @Override public void joinChat(final UUID chatId, final String sessionId, final String userName) throws MageException { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); ChatManager.getInstance().joinChat(chatId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } + } + }); } @Override public void leaveChat(final UUID chatId, final String sessionId) throws MageException { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - ChatManager.getInstance().leaveChat(chatId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + ChatManager.getInstance().leaveChat(chatId, userId); + } + }); } @Override @@ -472,46 +404,22 @@ public class MageServerImpl implements MageServer { @Override public void swapSeats(final String sessionId, final UUID roomId, final UUID tableId, final int seatNum1, final int seatNum2) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - TableManager.getInstance().swapSeats(tableId, userId, seatNum1, seatNum2); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + TableManager.getInstance().swapSeats(tableId, userId, seatNum1, seatNum2); + } + }); } @Override public void leaveTable(final String sessionId, final UUID roomId, final UUID tableId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - GamesRoomManager.getInstance().getRoom(roomId).leaveTable(userId, tableId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + GamesRoomManager.getInstance().getRoom(roomId).leaveTable(userId, tableId); + } + }); } @Override @@ -527,68 +435,32 @@ public class MageServerImpl implements MageServer { @Override public void joinGame(final UUID gameId, final String sessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - GameManager.getInstance().joinGame(gameId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + GameManager.getInstance().joinGame(gameId, userId); + } + }); } @Override public void joinDraft(final UUID draftId, final String sessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - DraftManager.getInstance().joinDraft(draftId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + DraftManager.getInstance().joinDraft(draftId, userId); + } + }); } @Override public void joinTournament(final UUID tournamentId, final String sessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - TournamentManager.getInstance().joinTournament(tournamentId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + TournamentManager.getInstance().joinTournament(tournamentId, userId); + } + }); } @Override @@ -613,91 +485,45 @@ public class MageServerImpl implements MageServer { return null; } - @Override - public void sendPlayerUUID(final UUID gameId, final String sessionId, final UUID data) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - User user = SessionManager.getInstance().getUser(sessionId); - user.sendPlayerUUID(gameId, data); - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } - } + @Override + public void sendPlayerUUID(final UUID gameId, final String sessionId, final UUID data) throws MageException { + execute(sessionId, new Action() { + public void execute() { + User user = SessionManager.getInstance().getUser(sessionId); + user.sendPlayerUUID(gameId, data); + } + }); + } @Override - public void sendPlayerString(final UUID gameId, final String sessionId, final String data) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - User user = SessionManager.getInstance().getUser(sessionId); - user.sendPlayerString(gameId, data); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + public void sendPlayerString(final UUID gameId, final String sessionId, final String data) throws MageException { + execute(sessionId, new Action() { + public void execute() { + User user = SessionManager.getInstance().getUser(sessionId); + user.sendPlayerString(gameId, data); + } + }); } @Override public void sendPlayerBoolean(final UUID gameId, final String sessionId, final Boolean data) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - User user = SessionManager.getInstance().getUser(sessionId); - user.sendPlayerBoolean(gameId, data); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + User user = SessionManager.getInstance().getUser(sessionId); + user.sendPlayerBoolean(gameId, data); + } + }); } @Override public void sendPlayerInteger(final UUID gameId, final String sessionId, final Integer data) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - User user = SessionManager.getInstance().getUser(sessionId); - user.sendPlayerInteger(gameId, data); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } - } + execute(sessionId, new Action() { + public void execute() { + User user = SessionManager.getInstance().getUser(sessionId); + user.sendPlayerInteger(gameId, data); + } + }); + } @Override public DraftPickView sendCardPick(final UUID draftId, final String sessionId, final UUID cardPick) throws MageException { @@ -713,27 +539,15 @@ public class MageServerImpl implements MageServer { return null; } - @Override - public void concedeGame(final UUID gameId, final String sessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - GameManager.getInstance().concedeGame(gameId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } - } + @Override + public void concedeGame(final UUID gameId, final String sessionId) throws MageException { + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + GameManager.getInstance().concedeGame(gameId, userId); + } + }); + } @Override public boolean watchTable(String sessionId, UUID roomId, UUID tableId) throws MageException { @@ -749,181 +563,85 @@ public class MageServerImpl implements MageServer { return false; } - @Override - public void watchGame(final UUID gameId, final String sessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - GameManager.getInstance().watchGame(gameId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } - } + @Override + public void watchGame(final UUID gameId, final String sessionId) throws MageException { + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + GameManager.getInstance().watchGame(gameId, userId); + } + }); + } - @Override - public void stopWatching(final UUID gameId, final String sessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - GameManager.getInstance().stopWatching(gameId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } - } + @Override + public void stopWatching(final UUID gameId, final String sessionId) throws MageException { + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + GameManager.getInstance().stopWatching(gameId, userId); + } + }); + } @Override public void replayGame(final UUID gameId, final String sessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - ReplayManager.getInstance().replayGame(gameId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + ReplayManager.getInstance().replayGame(gameId, userId); + } + }); } - @Override - public void startReplay(final UUID gameId, final String sessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - ReplayManager.getInstance().startReplay(gameId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } - } + @Override + public void startReplay(final UUID gameId, final String sessionId) throws MageException { + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + ReplayManager.getInstance().startReplay(gameId, userId); + } + }); + } @Override public void stopReplay(final UUID gameId, final String sessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - ReplayManager.getInstance().stopReplay(gameId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + ReplayManager.getInstance().stopReplay(gameId, userId); + } + }); } @Override - public void nextPlay(final UUID gameId, final String sessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - ReplayManager.getInstance().nextPlay(gameId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } - } + public void nextPlay(final UUID gameId, final String sessionId) throws MageException { + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + ReplayManager.getInstance().nextPlay(gameId, userId); + } + }); + } @Override public void previousPlay(final UUID gameId, final String sessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - ReplayManager.getInstance().previousPlay(gameId, userId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + ReplayManager.getInstance().previousPlay(gameId, userId); + } + }); } - @Override - public void skipForward(final UUID gameId, final String sessionId, final int moves) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - ReplayManager.getInstance().skipForward(gameId, userId, moves); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } - } + @Override + public void skipForward(final UUID gameId, final String sessionId, final int moves) throws MageException { + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + ReplayManager.getInstance().skipForward(gameId, userId, moves); + } + }); + } @Override public ServerState getServerState() throws MageException { @@ -942,29 +660,17 @@ public class MageServerImpl implements MageServer { return null; } - @Override - public void cheat(final UUID gameId, final String sessionId, final UUID playerId, final DeckCardLists deckList) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (testMode) { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - GameManager.getInstance().cheat(gameId, userId, playerId, deckList); - } - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } - } + @Override + public void cheat(final UUID gameId, final String sessionId, final UUID playerId, final DeckCardLists deckList) throws MageException { + execute(sessionId, new Action() { + public void execute() { + if (testMode) { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + GameManager.getInstance().cheat(gameId, userId, playerId, deckList); + } + } + }); + } @Override public boolean cheat(final UUID gameId, final String sessionId, final UUID playerId, final String cardName) throws MageException { @@ -1005,43 +711,21 @@ public class MageServerImpl implements MageServer { @Override public void disconnectUser(final String sessionId, final String userSessionId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - SessionManager.getInstance().disconnectUser(sessionId, userSessionId); - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + SessionManager.getInstance().disconnectUser(sessionId, userSessionId); + } + }); } - + @Override public void removeTable(final String sessionId, final UUID tableId) throws MageException { - if (SessionManager.getInstance().isValidSession(sessionId)) { - try { - callExecutor.execute( - new Runnable() { - @Override - public void run() { - if (SessionManager.getInstance().isValidSession(sessionId)) { - UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); - TableManager.getInstance().removeTable(userId, tableId); - } - } - } - ); - } - catch (Exception ex) { - handleException(ex); - } - } + execute(sessionId, new Action() { + public void execute() { + UUID userId = SessionManager.getInstance().getSession(sessionId).getUserId(); + TableManager.getInstance().removeTable(userId, tableId); + } + }); } @Override @@ -1056,4 +740,24 @@ public class MageServerImpl implements MageServer { } return null; } + + protected void execute(final String sessionId, final Action action) throws MageException { + if (SessionManager.getInstance().isValidSession(sessionId)) { + try { + callExecutor.execute( + new Runnable() { + @Override + public void run() { + if (SessionManager.getInstance().isValidSession(sessionId)) { + action.execute(); + } + } + } + ); + } + catch (Exception ex) { + handleException(ex); + } + } + } } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java b/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java index f8bd424494..d9f2e48ac8 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/AkkiLavarunner.java @@ -1,5 +1,6 @@ package mage.sets.championsofkamigawa; +import java.util.UUID; import mage.Constants; import mage.MageInt; import mage.ObjectColor; @@ -11,7 +12,6 @@ import mage.abilities.decorator.ConditionalContinousEffect; import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.CopyTokenEffect; import mage.abilities.effects.common.FlipSourceEffect; -import mage.abilities.effects.common.UntapAllLandsControllerEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.ProtectionAbility; import mage.cards.Card; @@ -23,8 +23,6 @@ import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; import mage.game.permanent.token.Token; -import java.util.UUID; - /** * @author Loki */ diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/BloodthirstyOgre.java b/Mage.Sets/src/mage/sets/championsofkamigawa/BloodthirstyOgre.java index 336983b76e..39adb244d0 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/BloodthirstyOgre.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/BloodthirstyOgre.java @@ -76,7 +76,8 @@ public class BloodthirstyOgre extends CardImpl { this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(new DevotionCounter()),new TapSourceCost())); // {T}: Target creature gets -X/-X until end of turn, where X is the number of devotion counters on Bloodthirsty Ogre. Activate this ability only if you control a Demon. DynamicValue devotionCounters = new SignInversionDynamicValue(new CountersCount(CounterType.DEVOTION)); - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD,new BoostTargetEffect(devotionCounters,devotionCounters, Duration.EndOfTurn),new TapSourceCost()); + Ability ability; + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(devotionCounters,devotionCounters, Duration.EndOfTurn, true),new TapSourceCost()); ability.addCost(new ControlPermanentCost(filter)); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfCleansingFire.java b/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfCleansingFire.java index d2264ca594..1bd1cfa704 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfCleansingFire.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfCleansingFire.java @@ -58,6 +58,8 @@ public class HondenOfCleansingFire extends CardImpl { this.supertype.add("Legendary"); this.subtype.add("Shrine"); this.color.setWhite(true); + + // At the beginning of your upkeep, you gain 2 life for each Shrine you control. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new GainLifeEffect(new PermanentsOnBattlefieldCount(filter, 2)), Constants.TargetController.YOU, false)); } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfInfiniteRage.java b/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfInfiniteRage.java index de4b2bbfe5..a8021235ac 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfInfiniteRage.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfInfiniteRage.java @@ -29,7 +29,6 @@ package mage.sets.championsofkamigawa; import java.util.UUID; - import mage.Constants; import mage.Constants.CardType; import mage.Constants.Rarity; @@ -60,7 +59,9 @@ public class HondenOfInfiniteRage extends CardImpl { this.expansionSetCode = "CHK"; this.supertype.add("Legendary"); this.subtype.add("Shrine"); - this.color.setRed(true); + this.color.setRed(true); + + // At the beginning of your upkeep, Honden of Infinite Rage deals damage to target creature or player equal to the number of Shrines you control. Ability ability = new BeginningOfUpkeepTriggeredAbility(new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter)), Constants.TargetController.YOU, false); ability.addTarget(new TargetCreatureOrPlayer()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfNightsReach.java b/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfNightsReach.java index 12bb71ff35..2cb62296ac 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfNightsReach.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfNightsReach.java @@ -29,7 +29,6 @@ package mage.sets.championsofkamigawa; import java.util.UUID; - import mage.Constants; import mage.Constants.CardType; import mage.Constants.Rarity; @@ -47,7 +46,7 @@ import mage.target.common.TargetOpponent; */ public class HondenOfNightsReach extends CardImpl { - final static FilterControlledPermanent filter = new FilterControlledPermanent("shrine"); + final static FilterControlledPermanent filter = new FilterControlledPermanent("Shrine"); static { filter.getSubtype().add("Shrine"); @@ -60,6 +59,8 @@ public class HondenOfNightsReach extends CardImpl { this.supertype.add("Legendary"); this.subtype.add("Shrine"); this.color.setBlack(true); + + // At the beginning of your upkeep, target opponent discards a card for each Shrine you control. Ability ability = new BeginningOfUpkeepTriggeredAbility(new DiscardTargetEffect(new PermanentsOnBattlefieldCount(filter)), Constants.TargetController.YOU, false); ability.addTarget(new TargetOpponent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfSeeingWinds.java b/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfSeeingWinds.java index e04df636b5..814fb4387f 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfSeeingWinds.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/HondenOfSeeingWinds.java @@ -58,6 +58,8 @@ public class HondenOfSeeingWinds extends CardImpl { this.supertype.add("Legendary"); this.subtype.add("Shrine"); this.color.setBlue(true); + + // At the beginning of your upkeep, draw a card for each Shrine you control. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DrawCardControllerEffect(new PermanentsOnBattlefieldCount(filter)), Constants.TargetController.YOU, false)); } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KashiTribeReaver.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KashiTribeReaver.java new file mode 100644 index 0000000000..d44dffb56c --- /dev/null +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KashiTribeReaver.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.sets.championsofkamigawa; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.abilities.effects.common.SkipNextUntapTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; + +/** + * + * @author LevelX + */ +public class KashiTribeReaver extends CardImpl { + + public KashiTribeReaver(UUID ownerId) { + super(ownerId, 220, "Kashi-Tribe Reaver", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{G}"); + this.expansionSetCode = "CHK"; + this.subtype.add("Snake"); + this.subtype.add("Warrior"); + + this.color.setGreen(true); + this.power = new MageInt(3); + this.toughness = new MageInt(2); + + // Whenever Kashi-Tribe Reaver deals combat damage to a creature, tap that creature and it doesn't untap during its controller's next untap step. + Ability ability; + ability = new DealsCombatDamageToACreatureTriggeredAbility(new TapTargetEffect("that creature"), false, true); + ability.addEffect(new SkipNextUntapTargetEffect("and it")); + this.addAbility(ability); + // {1}{G}: Regenerate Kashi-Tribe Reaver. + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{1}{G}"))); + } + + public KashiTribeReaver(final KashiTribeReaver card) { + super(card); + } + + @Override + public KashiTribeReaver copy() { + return new KashiTribeReaver(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KashiTribeWarriors.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KashiTribeWarriors.java new file mode 100644 index 0000000000..cfe32a1f6d --- /dev/null +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KashiTribeWarriors.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 mage.sets.championsofkamigawa; + +import java.util.UUID; + +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility; +import mage.abilities.effects.common.SkipNextUntapTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; + +/** + * + * @author LevelX + */ +public class KashiTribeWarriors extends CardImpl { + + public KashiTribeWarriors(UUID ownerId) { + super(ownerId, 221, "Kashi-Tribe Warriors", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + this.expansionSetCode = "CHK"; + this.subtype.add("Snake"); + this.subtype.add("Warrior"); + + this.color.setGreen(true); + this.power = new MageInt(2); + this.toughness = new MageInt(4); + + // Whenever Kashi-Tribe Reaver deals combat damage to a creature, tap that creature and it doesn't untap during its controller's next untap step. + Ability ability; + ability = new DealsCombatDamageToACreatureTriggeredAbility(new TapTargetEffect("that creature"), false, true); + ability.addEffect(new SkipNextUntapTargetEffect("and it")); + this.addAbility(ability); + } + + public KashiTribeWarriors(final KashiTribeWarriors card) { + super(card); + } + + @Override + public KashiTribeWarriors copy() { + return new KashiTribeWarriors(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/KikuNightsFlower.java b/Mage.Sets/src/mage/sets/championsofkamigawa/KikuNightsFlower.java new file mode 100644 index 0000000000..8773bb717e --- /dev/null +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KikuNightsFlower.java @@ -0,0 +1,111 @@ +/* + * 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.championsofkamigawa; + +import java.util.UUID; +import mage.Constants; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author LevelX + */ +public class KikuNightsFlower extends CardImpl { + + public KikuNightsFlower (UUID ownerId) { + super(ownerId, 121, "Kiku, Night's Flower", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{B}{B}"); + this.expansionSetCode = "CHK"; + this.supertype.add("Legendary"); + this.subtype.add("Human"); + this.subtype.add("Assassin"); + this.color.setBlack(true); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {2}{B}{B}, {T}: Target creature deals damage to itself equal to its power. + Ability ability; + ability = new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new KikuNightsFlowerEffect(), + new ManaCostsImpl("{2}{B}{B}") + ); + ability.addTarget(new TargetCreaturePermanent()); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + public KikuNightsFlower (final KikuNightsFlower card) { + super(card); + } + + @Override + public KikuNightsFlower copy() { + return new KikuNightsFlower(this); + } +} + +class KikuNightsFlowerEffect extends OneShotEffect { + + public KikuNightsFlowerEffect() { + super(Constants.Outcome.Damage); + this.staticText = "Target creature deals damage to itself equal to its power"; + } + + public KikuNightsFlowerEffect(final KikuNightsFlowerEffect effect) { + super(effect); + } + + @Override + public KikuNightsFlowerEffect copy() { + return new KikuNightsFlowerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(targetPointer.getFirst(source)); + if (permanent != null) { + permanent.damage(permanent.getPower().getValue(), permanent.getId(), game, true, false); + 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 d48ff522b8..c42806d12d 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/KondasBanner.java @@ -69,10 +69,10 @@ public class KondasBanner extends CardImpl { this.subtype.add("Equipment"); // Creatures that share a color with equipped creature get +1/+1. - this.addAbility(new SimpleStaticAbility(Constants.Zone.BATTLEFIELD, new KodasBannerColorBoostEffect())); + this.addAbility(new SimpleStaticAbility(Constants.Zone.BATTLEFIELD, new KondasBannerColorBoostEffect())); // Creatures that share a creature type with equipped creature get +1/+1. - this.addAbility(new SimpleStaticAbility(Constants.Zone.BATTLEFIELD, new KodasBannerTypeBoostEffect())); + this.addAbility(new SimpleStaticAbility(Constants.Zone.BATTLEFIELD, new KondasBannerTypeBoostEffect())); // Konda's Banner can be attached only to a legendary creature. // Equip {2} @@ -93,16 +93,16 @@ public class KondasBanner extends CardImpl { } } -class KodasBannerTypeBoostEffect extends BoostAllEffect { +class KondasBannerTypeBoostEffect extends BoostAllEffect { private static final String effectText = "Creatures that share a creature type with equipped creature get +1/+1"; - KodasBannerTypeBoostEffect() { + KondasBannerTypeBoostEffect() { super(1,1, Constants.Duration.WhileOnBattlefield, new FilterCreaturePermanent(), false); staticText = effectText; } - KodasBannerTypeBoostEffect(KodasBannerTypeBoostEffect effect) { + KondasBannerTypeBoostEffect(KondasBannerTypeBoostEffect effect) { super(effect); } @@ -113,38 +113,40 @@ class KodasBannerTypeBoostEffect 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 (CardUtil.shareSubtypes(perm, equipedCreature) || perm.getAbilities().contains(ChangelingAbility.getInstance())) { - if (!this.affectedObjectsSet || objects.contains(perm.getId())) { - perm.addPower(power.calculate(game, source)); - perm.addToughness(toughness.calculate(game, source)); - } - - } - } - return true; + if (equipedCreature != null) { + for (Permanent perm: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { + if (CardUtil.shareSubtypes(perm, equipedCreature) || perm.getAbilities().contains(ChangelingAbility.getInstance())) { + if (!this.affectedObjectsSet || objects.contains(perm.getId())) { + perm.addPower(power.calculate(game, source)); + perm.addToughness(toughness.calculate(game, source)); + } + + } + } + return true; + } } return false; } @Override - public KodasBannerTypeBoostEffect copy() { - return new KodasBannerTypeBoostEffect(this); + public KondasBannerTypeBoostEffect copy() { + return new KondasBannerTypeBoostEffect(this); } } -class KodasBannerColorBoostEffect extends BoostAllEffect { +class KondasBannerColorBoostEffect extends BoostAllEffect { private static final String effectText = "Creatures that share a color with equipped creature get +1/+1."; - KodasBannerColorBoostEffect() { + KondasBannerColorBoostEffect() { super(1,1, Constants.Duration.WhileOnBattlefield, new FilterCreaturePermanent(), false); staticText = effectText; } - KodasBannerColorBoostEffect(KodasBannerColorBoostEffect effect) { + KondasBannerColorBoostEffect(KondasBannerColorBoostEffect effect) { super(effect); } @@ -170,8 +172,8 @@ class KodasBannerColorBoostEffect extends BoostAllEffect { } @Override - public KodasBannerColorBoostEffect copy() { - return new KodasBannerColorBoostEffect(this); + public KondasBannerColorBoostEffect copy() { + return new KondasBannerColorBoostEffect(this); } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/MarrowGnawer.java b/Mage.Sets/src/mage/sets/championsofkamigawa/MarrowGnawer.java new file mode 100644 index 0000000000..3cc133fba7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/MarrowGnawer.java @@ -0,0 +1,110 @@ +/* + * 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.championsofkamigawa; + +import java.util.UUID; +import mage.Constants; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.continious.GainAbilityAllEffect; +import mage.abilities.keyword.FearAbility; +import mage.cards.CardImpl; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.permanent.token.Token; +import mage.target.common.TargetControlledPermanent; + +/** + * + * @author LevelX + */ +public class MarrowGnawer extends CardImpl { + + private static final FilterCreaturePermanent filterFear = new FilterCreaturePermanent("Rat creatures"); + private static final FilterControlledCreaturePermanent filterSacrifice = new FilterControlledCreaturePermanent("a Rat"); + private static final FilterControlledCreaturePermanent filter3 = new FilterControlledCreaturePermanent("Rats you control"); + + static { + filterFear.getSubtype().add("Rat"); + filterSacrifice.getSubtype().add("Rat"); + filter3.getSubtype().add("Rat"); + } + + public MarrowGnawer (UUID ownerId) { + super(ownerId, 124, "Marrow-Gnawer", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + this.expansionSetCode = "CHK"; + this.subtype.add("Rat"); + this.subtype.add("Rogue"); + this.color.setBlack(true); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Rat creatures have fear. (They can't be blocked except by artifact creatures and/or black creatures.) + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAllEffect(FearAbility.getInstance(), Constants.Duration.WhileOnBattlefield, filterFear))); + + // {T}, Sacrifice a Rat: Put X 1/1 black Rat creature tokens onto the battlefield, where X is the number of Rats you control. + Ability ability; + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new RatToken(),new PermanentsOnBattlefieldCount(filter3)), new SacrificeTargetCost(new TargetControlledPermanent(filterSacrifice))); + ability.addCost(new TapSourceCost()); + this.addAbility(ability); + } + + public MarrowGnawer (final MarrowGnawer card) { + super(card); + } + + @Override + public MarrowGnawer copy() { + return new MarrowGnawer(this); + } + +} + +class RatToken extends Token { + + public RatToken() { + super("Rat", "1/1 black Rat creature token"); + cardType.add(CardType.CREATURE); + color = ObjectColor.BLACK; + subtype.add("Rat"); + power = new MageInt(1); + toughness = new MageInt(1); + } + +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/MatsuTribeDecoy.java b/Mage.Sets/src/mage/sets/championsofkamigawa/MatsuTribeDecoy.java new file mode 100644 index 0000000000..d3bd058d79 --- /dev/null +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/MatsuTribeDecoy.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.championsofkamigawa; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.MustBlockSourceTargetEffect; +import mage.abilities.effects.common.SkipNextUntapTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; +import mage.filter.common.FilterCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author LevelX + */ +public class MatsuTribeDecoy extends CardImpl { + + public MatsuTribeDecoy(UUID ownerId) { + super(ownerId, 227, "Matsu-Tribe Decoy", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{G}"); + this.expansionSetCode = "CHK"; + this.subtype.add("Snake"); + this.subtype.add("Warrior"); + + this.color.setGreen(true); + this.power = new MageInt(1); + this.toughness = new MageInt(3); + + // {2}{G}: Target creature blocks Matsu-Tribe Decoy this turn if able. + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MustBlockSourceTargetEffect(), new ManaCostsImpl("{2}{G}")); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + // Whenever Kashi-Tribe Reaver deals combat damage to a creature, tap that creature and it doesn't untap during its controller's next untap step. + Ability ability2; + ability2 = new DealsCombatDamageToACreatureTriggeredAbility(new TapTargetEffect("that creature"), false, true); + ability2.addEffect(new SkipNextUntapTargetEffect("and it")); + this.addAbility(ability2); + } + + public MatsuTribeDecoy(final MatsuTribeDecoy card) { + super(card); + } + + @Override + public MatsuTribeDecoy copy() { + return new MatsuTribeDecoy(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/OrochiEggwatcher.java b/Mage.Sets/src/mage/sets/championsofkamigawa/OrochiEggwatcher.java new file mode 100644 index 0000000000..0fdefc0022 --- /dev/null +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/OrochiEggwatcher.java @@ -0,0 +1,117 @@ +/* + * + * 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.championsofkamigawa; + +import java.util.UUID; +import mage.Constants; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.common.ControlsPermanentCondition; +import mage.abilities.condition.common.FlippedCondition; +import mage.abilities.costs.common.SacrificeTargetCost; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalContinousEffect; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.CopyTokenEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.effects.common.FlipSourceEffect; +import mage.abilities.effects.common.continious.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.filter.common.FilterControlledCreaturePermanent; +import mage.game.permanent.token.SnakeToken; +import mage.game.permanent.token.Token; +import mage.target.common.TargetControlledCreaturePermanent; +import mage.target.common.TargetCreaturePermanent; + +/** + * @author LevelX + */ +public class OrochiEggwatcher extends CardImpl { + + public OrochiEggwatcher(UUID ownerId) { + super(ownerId, 233, "Orochi Eggwatcher", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{G}"); + this.expansionSetCode = "CHK"; + this.subtype.add("Snake"); + this.subtype.add("Shaman"); + this.color.setGreen(true); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + + // {2}{G}, {T}: Put a 1/1 green Snake creature token onto the battlefield. If you control ten or more creatures, flip Orochi Eggwatcher. + Ability ability; + ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new SnakeToken()),new ManaCostsImpl("{2}{G}")); + ability.addCost(new TapSourceCost()); + ability.addEffect(new ConditionalOneShotEffect(new FlipSourceEffect(), + new ControlsPermanentCondition(new FilterControlledCreaturePermanent(),ControlsPermanentCondition.CountType.MORE_THAN, 9),"If you control ten or more creatures, flip {this}")); + this.addAbility(ability); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinousEffect(new CopyTokenEffect(new ShidakoBroodmistress()), FlippedCondition.getInstance(), ""))); + } + + public OrochiEggwatcher(final OrochiEggwatcher card) { + super(card); + } + + @Override + public OrochiEggwatcher copy() { + return new OrochiEggwatcher(this); + } +} + +class ShidakoBroodmistress extends Token { + + ShidakoBroodmistress() { + super("Shidako, Broodmistress", ""); + supertype.add("Legendary"); + cardType.add(Constants.CardType.CREATURE); + color.setGreen(true); + subtype.add("Snake"); + subtype.add("Shaman"); + power = new MageInt(3); + toughness = new MageInt(3); + // {G}, Sacrifice a creature: Target creature gets +3/+3 until end of turn. + Ability ability; + ability = new SimpleActivatedAbility( + Zone.BATTLEFIELD, + new BoostTargetEffect(3,3, Duration.EndOfTurn), + new ManaCostsImpl("{G}")); + ability.addCost(new SacrificeTargetCost(new TargetControlledCreaturePermanent())); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } +} + diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/OrochiRanger.java b/Mage.Sets/src/mage/sets/championsofkamigawa/OrochiRanger.java new file mode 100644 index 0000000000..36425bbb5a --- /dev/null +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/OrochiRanger.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 mage.sets.championsofkamigawa; + +import java.util.UUID; + +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility; +import mage.abilities.effects.common.SkipNextUntapTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.cards.CardImpl; + +/** + * + * @author LevelX + */ +public class OrochiRanger extends CardImpl { + + public OrochiRanger(UUID ownerId) { + super(ownerId, 235, "Orochi Ranger", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{G}"); + this.expansionSetCode = "CHK"; + this.subtype.add("Snake"); + this.subtype.add("Warrior"); + + this.color.setGreen(true); + this.power = new MageInt(2); + this.toughness = new MageInt(1); + + // Whenever Orochi Ranger deals combat damage to a creature, tap that creature and it doesn't untap during its controller's next untap step. + Ability ability; + ability = new DealsCombatDamageToACreatureTriggeredAbility(new TapTargetEffect("that creature"), false, true); + ability.addEffect(new SkipNextUntapTargetEffect("and it")); + this.addAbility(ability); + } + + public OrochiRanger(final OrochiRanger card) { + super(card); + } + + @Override + public OrochiRanger copy() { + return new OrochiRanger(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/ShisatoWhisperingHunter.java b/Mage.Sets/src/mage/sets/championsofkamigawa/ShisatoWhisperingHunter.java new file mode 100644 index 0000000000..4e1199f9d1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/ShisatoWhisperingHunter.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.championsofkamigawa; + +import java.util.UUID; +import mage.Constants; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.Constants.TargetController; +import mage.MageInt; +import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.effects.common.SacrificeControllerEffect; +import mage.abilities.effects.common.SkipNextPlayerUntapStepEffect; +import mage.cards.CardImpl; +import mage.filter.common.FilterControlledCreaturePermanent; + +/** + * + * @author LevelX + */ +public class ShisatoWhisperingHunter extends CardImpl { + + + private final static FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Snake"); + + static { + filter.getSubtype().add("Snake"); + filter.setTargetController(TargetController.YOU); + } + + public ShisatoWhisperingHunter(UUID ownerId) { + super(ownerId, 242, "Shisato, Whispering Hunter", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{G}"); + this.expansionSetCode = "CHK"; + this.supertype.add("Legendary"); + this.subtype.add("Snake"); + this.subtype.add("Warrior"); + + this.color.setGreen(true); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // At the beginning of your upkeep, sacrifice a Snake. + this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeControllerEffect(filter, 1,""), Constants.TargetController.YOU, false)); + // Whenever Shisato, Whispering Hunter deals combat damage to a player, that player skips his or her next untap step. + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new SkipNextPlayerUntapStepEffect("that "),false, true)); + } + + public ShisatoWhisperingHunter(final ShisatoWhisperingHunter card) { + super(card); + } + + @Override + public ShisatoWhisperingHunter copy() { + return new ShisatoWhisperingHunter(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/SosukeSonOfSeshiro.java b/Mage.Sets/src/mage/sets/championsofkamigawa/SosukeSonOfSeshiro.java new file mode 100644 index 0000000000..248eef79a7 --- /dev/null +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/SosukeSonOfSeshiro.java @@ -0,0 +1,158 @@ +/* + * 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.championsofkamigawa; + +import java.util.UUID; +import mage.Constants; +import mage.Constants.CardType; +import mage.Constants.Outcome; +import mage.Constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.effects.common.continious.BoostControlledEffect; +import mage.cards.CardImpl; +import mage.filter.Filter; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.events.DamagedCreatureEvent; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author LevelX + */ +public class SosukeSonOfSeshiro extends CardImpl { + + private final static FilterCreaturePermanent filter = new FilterCreaturePermanent("Snake creatures"); + + static { + filter.getSubtype().add("Snake"); + filter.setScopeSubtype(Filter.ComparisonScope.Any); + } + + public SosukeSonOfSeshiro(UUID ownerId) { + super(ownerId, 244, "Sosuke, Son of Seshiro", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + this.expansionSetCode = "CHK"; + this.supertype.add("Legendary"); + this.subtype.add("Snake"); + this.subtype.add("Warrior"); + + this.color.setGreen(true); + this.power = new MageInt(3); + this.toughness = new MageInt(4); + + // Other Snake creatures you control get +1/+0. + this.addAbility(new SimpleStaticAbility(Constants.Zone.BATTLEFIELD, new BoostControlledEffect(1, 0, Constants.Duration.WhileOnBattlefield, filter, true))); + // Whenever a Warrior you control deals combat damage to a creature, destroy that creature at end of combat. + this.addAbility(new SosukeSonOfSeshiroTriggeredAbility()); + } + + public SosukeSonOfSeshiro(final SosukeSonOfSeshiro card) { + super(card); + } + + @Override + public SosukeSonOfSeshiro copy() { + return new SosukeSonOfSeshiro(this); + } +} + +class SosukeSonOfSeshiroTriggeredAbility extends TriggeredAbilityImpl { + + SosukeSonOfSeshiroTriggeredAbility() { + super(Constants.Zone.BATTLEFIELD, new SosukeSonOfSeshiroEffect()); + } + + SosukeSonOfSeshiroTriggeredAbility(final SosukeSonOfSeshiroTriggeredAbility ability) { + super(ability); + } + + @Override + public SosukeSonOfSeshiroTriggeredAbility copy() { + return new SosukeSonOfSeshiroTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event instanceof DamagedCreatureEvent) { + if (((DamagedCreatureEvent) event).isCombatDamage()) { + Permanent sourceCreature = game.getPermanent(event.getSourceId()); + Permanent targetCreature = game.getPermanent(event.getTargetId()); + if (sourceCreature != null && sourceCreature.getControllerId().equals(this.getControllerId()) + && targetCreature != null && sourceCreature.getSubtype().contains("Warrior")) { + this.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); + return true; + } + } + } + return false; + } + + @Override + public String getRule() { + return "Whenever a Warrior you control deals combat damage to a creature, destroy that creature at end of combat."; + } +} + +class SosukeSonOfSeshiroEffect extends OneShotEffect { + + SosukeSonOfSeshiroEffect() { + super(Outcome.DestroyPermanent); + staticText = "destroy that creature at end of combat"; + } + + SosukeSonOfSeshiroEffect(final SosukeSonOfSeshiroEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent targetCreature = game.getPermanent(this.getTargetPointer().getFirst(source)); + if (targetCreature != null) { + AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect()); + delayedAbility.setSourceId(source.getSourceId()); + delayedAbility.setControllerId(source.getControllerId()); + delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(targetCreature.getId())); + game.addDelayedTriggeredAbility(delayedAbility); + return true; + } + return false; + } + + @Override + public SosukeSonOfSeshiroEffect copy() { + return new SosukeSonOfSeshiroEffect(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/StrengthOfCedars.java b/Mage.Sets/src/mage/sets/championsofkamigawa/StrengthOfCedars.java index 5b3b8c790b..75c35cb831 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/StrengthOfCedars.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/StrengthOfCedars.java @@ -59,7 +59,7 @@ public class StrengthOfCedars extends CardImpl { this.subtype.add("Arcane"); this.color.setGreen(true); this.getSpellAbility().addEffect(new BoostTargetEffect(new PermanentsOnBattlefieldCount(filter), - new PermanentsOnBattlefieldCount(filter), Constants.Duration.EndOfTurn)); + new PermanentsOnBattlefieldCount(filter), Constants.Duration.EndOfTurn, true)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/YoseiTheMorningStar.java b/Mage.Sets/src/mage/sets/championsofkamigawa/YoseiTheMorningStar.java index 96601a1e9e..2c2e8bdb7d 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/YoseiTheMorningStar.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/YoseiTheMorningStar.java @@ -28,23 +28,20 @@ package mage.sets.championsofkamigawa; import java.util.UUID; -import mage.Constants; import mage.Constants.CardType; import mage.Constants.Outcome; -import mage.Constants.PhaseStep; import mage.Constants.Rarity; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.SkipNextPlayerUntapStepEffect; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.turn.TurnMod; import mage.players.Player; import mage.target.Target; import mage.target.TargetPermanent; @@ -69,7 +66,7 @@ public class YoseiTheMorningStar extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // When Yosei, the Morning Star dies, target player skips his or her next untap step. Tap up to five target permanents that player controls. - Ability ability = new DiesTriggeredAbility(new SkipNextUntapStepTargetEffect()); + Ability ability = new DiesTriggeredAbility(new SkipNextPlayerUntapStepEffect()); ability.addTarget(new TargetPlayer()); ability.addTarget(new YoseiTheMorningStarTarget()); ability.addEffect(new YoseiTheMorningStarTapEffect()); @@ -86,33 +83,6 @@ public class YoseiTheMorningStar extends CardImpl { } } -class SkipNextUntapStepTargetEffect extends OneShotEffect { - - public SkipNextUntapStepTargetEffect() { - super(Constants.Outcome.Detriment); - staticText = "target player skips his or her next untap step"; - } - - public SkipNextUntapStepTargetEffect(SkipNextUntapStepTargetEffect effect) { - super(effect); - } - - @Override - public boolean apply(Game game, Ability source) { - UUID playerId = source.getFirstTarget(); - if (playerId != null) { - game.getState().getTurnMods().add(new TurnMod(playerId, PhaseStep.UNTAP)); - return true; - } - return false; - } - - @Override - public SkipNextUntapStepTargetEffect copy() { - return new SkipNextUntapStepTargetEffect(); - } -} - class YoseiTheMorningStarTarget extends TargetPermanent { public YoseiTheMorningStarTarget() { diff --git a/Mage.Sets/src/mage/sets/conflux/DragDown.java b/Mage.Sets/src/mage/sets/conflux/DragDown.java index cc9bf6af9f..13045084e4 100644 --- a/Mage.Sets/src/mage/sets/conflux/DragDown.java +++ b/Mage.Sets/src/mage/sets/conflux/DragDown.java @@ -49,7 +49,7 @@ public class DragDown extends CardImpl { this.color.setBlack(true); // Domain - Target creature gets -1/-1 until end of turn for each basic land type among lands you control. - this.getSpellAbility().addEffect(new BoostTargetEffect(new DomainValue(-1), new DomainValue(-1), Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new BoostTargetEffect(new DomainValue(-1), new DomainValue(-1), Duration.EndOfTurn, true)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/sets/conflux/MightOfAlara.java b/Mage.Sets/src/mage/sets/conflux/MightOfAlara.java index a132de6be9..aad1baf79a 100644 --- a/Mage.Sets/src/mage/sets/conflux/MightOfAlara.java +++ b/Mage.Sets/src/mage/sets/conflux/MightOfAlara.java @@ -50,7 +50,7 @@ public class MightOfAlara extends CardImpl { this.color.setGreen(true); // Domain - Target creature gets +1/+1 until end of turn for each basic land type among lands you control. - this.getSpellAbility().addEffect(new BoostTargetEffect(new DomainValue(), new DomainValue(), Constants.Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new BoostTargetEffect(new DomainValue(), new DomainValue(), Constants.Duration.EndOfTurn, true)); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); } diff --git a/Mage.Sets/src/mage/sets/eventide/CreakwoodGhoul.java b/Mage.Sets/src/mage/sets/eventide/CreakwoodGhoul.java index 7db5986ee8..7fbb5b3a72 100644 --- a/Mage.Sets/src/mage/sets/eventide/CreakwoodGhoul.java +++ b/Mage.Sets/src/mage/sets/eventide/CreakwoodGhoul.java @@ -28,7 +28,6 @@ package mage.sets.eventide; import java.util.UUID; - import mage.Constants; import mage.Constants.CardType; import mage.Constants.Rarity; @@ -41,6 +40,7 @@ import mage.abilities.effects.common.GainLifeEffect; import mage.cards.CardImpl; import mage.target.common.TargetCardInGraveyard; + /** * * @author Loki diff --git a/Mage.Sets/src/mage/sets/innistrad/DearlyDeparted.java b/Mage.Sets/src/mage/sets/innistrad/DearlyDeparted.java index 4b5a456d72..f3811f9003 100644 --- a/Mage.Sets/src/mage/sets/innistrad/DearlyDeparted.java +++ b/Mage.Sets/src/mage/sets/innistrad/DearlyDeparted.java @@ -27,6 +27,8 @@ */ package mage.sets.innistrad; + +import java.util.UUID; import mage.Constants; import mage.Constants.CardType; import mage.Constants.Rarity; @@ -40,14 +42,8 @@ import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.counters.CounterType; import mage.filter.Filter; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterControlledPermanent; -import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreaturePermanent; -import java.util.UUID; - /** * @author nantuko */ diff --git a/Mage.Sets/src/mage/sets/innistrad/ElderOfLaurels.java b/Mage.Sets/src/mage/sets/innistrad/ElderOfLaurels.java index ab5647abb9..62e66500cf 100644 --- a/Mage.Sets/src/mage/sets/innistrad/ElderOfLaurels.java +++ b/Mage.Sets/src/mage/sets/innistrad/ElderOfLaurels.java @@ -60,7 +60,7 @@ public class ElderOfLaurels extends CardImpl { // {3}{G}: Target creature gets +X/+X until end of turn, where X is the number of creatures you control. PermanentsOnBattlefieldCount amount = new PermanentsOnBattlefieldCount(new FilterControlledCreaturePermanent()); SimpleActivatedAbility ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, - new BoostTargetEffect(amount, amount, Duration.EndOfTurn), + new BoostTargetEffect(amount, amount, Duration.EndOfTurn, true), new ManaCostsImpl("{3}{G}")); ability.addTarget(new TargetCreaturePermanent()); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/sets/mirrodin/ClockworkBeetle.java b/Mage.Sets/src/mage/sets/mirrodin/ClockworkBeetle.java index fb8e36a572..73778208c2 100644 --- a/Mage.Sets/src/mage/sets/mirrodin/ClockworkBeetle.java +++ b/Mage.Sets/src/mage/sets/mirrodin/ClockworkBeetle.java @@ -58,7 +58,7 @@ public class ClockworkBeetle extends CardImpl { this.power = new MageInt(0); this.toughness = new MageInt(0); this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance(2)), "{this} enters the battlefield with two +1/+1 counters on it")); - this.addAbility(new AttacksOrBlocksTriggeredAbility(new ClockworkCondorEffect(), false)); + this.addAbility(new AttacksOrBlocksTriggeredAbility(new ClockworkBeetleEffect(), false)); } public ClockworkBeetle(final ClockworkBeetle card) { @@ -87,8 +87,8 @@ class ClockworkBeetleEffect extends OneShotEffect { if (p != null) { AtTheEndOfCombatDelayedTriggeredAbility ability = new AtTheEndOfCombatDelayedTriggeredAbility(new RemoveCounterSourceEffect(CounterType.P1P1.createInstance())); ability.setSourceId(source.getSourceId()); - ability.setControllerId(source.getControllerId()); - game.addDelayedTriggeredAbility(ability); + ability.setControllerId(source.getControllerId()); + game.addDelayedTriggeredAbility(ability); } return false; } diff --git a/Mage.Sets/src/mage/sets/riseoftheeldrazi/MightOfTheMasses.java b/Mage.Sets/src/mage/sets/riseoftheeldrazi/MightOfTheMasses.java index f933e29c53..4c3de2144a 100644 --- a/Mage.Sets/src/mage/sets/riseoftheeldrazi/MightOfTheMasses.java +++ b/Mage.Sets/src/mage/sets/riseoftheeldrazi/MightOfTheMasses.java @@ -51,7 +51,7 @@ public class MightOfTheMasses extends CardImpl { PermanentsOnBattlefieldCount value = new PermanentsOnBattlefieldCount(new FilterControlledCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent()); - this.getSpellAbility().addEffect(new BoostTargetEffect(value, value, Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new BoostTargetEffect(value, value, Duration.EndOfTurn, true)); } public MightOfTheMasses(final MightOfTheMasses card) { diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/KashiTribeElite.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/KashiTribeElite.java new file mode 100644 index 0000000000..1ece33843b --- /dev/null +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/KashiTribeElite.java @@ -0,0 +1,91 @@ +/* + * 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.saviorsofkamigawa; + +import java.util.UUID; +import mage.Constants; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.common.SkipNextUntapTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continious.GainAbilityControlledEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.ShroudAbility; +import mage.cards.CardImpl; +import mage.filter.Filter; +import mage.filter.common.FilterControlledPermanent; + +/** + * + * @author LevelX + */ +public class KashiTribeElite extends CardImpl { + + private final static FilterControlledPermanent filter = new FilterControlledPermanent("Legendary Snakes"); + + static { + filter.getSupertype().add("Legendary"); + filter.setScopeSupertype(Filter.ComparisonScope.Any); + filter.getSubtype().add("Snake"); + filter.setScopeSubtype(Filter.ComparisonScope.Any); + } + + public KashiTribeElite(UUID ownerId) { + super(ownerId, 135, "Kashi-Tribe Elite", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{1}{G}{G}"); + this.expansionSetCode = "SOK"; + this.subtype.add("Snake"); + this.subtype.add("Warrior"); + + this.color.setGreen(true); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + // Legendary Snakes you control have shroud. (They can't be the targets of spells or abilities.) + this.addAbility(new SimpleStaticAbility(Constants.Zone.BATTLEFIELD, new GainAbilityControlledEffect(ShroudAbility.getInstance(), Constants.Duration.WhileOnBattlefield, filter, false))); + + // Whenever Kashi-Tribe Elite deals combat damage to a creature, tap that creature and it doesn't untap during its controller's next untap step. + Ability ability; + ability = new DealsCombatDamageToACreatureTriggeredAbility(new TapTargetEffect("that creature"), false, true); + ability.addEffect(new SkipNextUntapTargetEffect("and it")); + this.addAbility(ability); + + } + + public KashiTribeElite(final KashiTribeElite card) { + super(card); + } + + @Override + public KashiTribeElite copy() { + return new KashiTribeElite(this); + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/saviorsofkamigawa/MatsuTribeBirdstalker.java b/Mage.Sets/src/mage/sets/saviorsofkamigawa/MatsuTribeBirdstalker.java new file mode 100644 index 0000000000..df6983909a --- /dev/null +++ b/Mage.Sets/src/mage/sets/saviorsofkamigawa/MatsuTribeBirdstalker.java @@ -0,0 +1,86 @@ +/* + * 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.saviorsofkamigawa; + +import java.util.UUID; +import mage.Constants; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.MageInt; +import mage.abilities.Ability; +import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.SkipNextUntapTargetEffect; +import mage.abilities.effects.common.TapTargetEffect; +import mage.abilities.effects.common.continious.GainAbilityControlledEffect; +import mage.abilities.effects.common.continious.GainAbilitySourceEffect; +import mage.abilities.keyword.ReachAbility; +import mage.abilities.keyword.ShroudAbility; +import mage.cards.CardImpl; +import mage.filter.Filter; +import mage.filter.common.FilterControlledPermanent; + +/** + * + * @author LevelX + */ +public class MatsuTribeBirdstalker extends CardImpl { + + public MatsuTribeBirdstalker(UUID ownerId) { + super(ownerId, 137, "Matsu-Tribe Birdstalker", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{G}{G}"); + this.expansionSetCode = "SOK"; + this.subtype.add("Snake"); + this.subtype.add("Warrior"); + + this.color.setGreen(true); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + + // Whenever Kashi-Tribe Elite deals combat damage to a creature, tap that creature and it doesn't untap during its controller's next untap step. + Ability ability; + ability = new DealsCombatDamageToACreatureTriggeredAbility(new TapTargetEffect("that creature"), false, true); + ability.addEffect(new SkipNextUntapTargetEffect("and it")); + this.addAbility(ability); + + // {G}: Matsu-Tribe Birdstalker gains reach until end of turn. (It can block creatures with flying.) + this.addAbility(new SimpleActivatedAbility(Constants.Zone.BATTLEFIELD, + new GainAbilitySourceEffect(ReachAbility.getInstance(), Constants.Duration.EndOfTurn), + new ManaCostsImpl("{G}"))); + } + + public MatsuTribeBirdstalker(final MatsuTribeBirdstalker card) { + super(card); + } + + @Override + public MatsuTribeBirdstalker copy() { + return new MatsuTribeBirdstalker(this); + } +} \ No newline at end of file diff --git a/Mage/src/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java b/Mage/src/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java index 3d8bb53995..877257bfa6 100644 --- a/Mage/src/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java +++ b/Mage/src/mage/abilities/common/BeginningOfUpkeepTriggeredAbility.java @@ -42,7 +42,7 @@ public class BeginningOfUpkeepTriggeredAbility extends TriggeredAbilityImpl { + + private boolean setTargetPointer; + + public DealsCombatDamageToACreatureTriggeredAbility(Effect effect, boolean optional) { + this(effect, optional, false); + } + + public DealsCombatDamageToACreatureTriggeredAbility(Effect effect, boolean optional, boolean setTargetPointer) { + super(Zone.BATTLEFIELD, effect, optional); + this.setTargetPointer = setTargetPointer; + } + + public DealsCombatDamageToACreatureTriggeredAbility(final DealsCombatDamageToACreatureTriggeredAbility ability) { + super(ability); + this.setTargetPointer = ability.setTargetPointer; + } + + @Override + public DealsCombatDamageToACreatureTriggeredAbility copy() { + return new DealsCombatDamageToACreatureTriggeredAbility(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + if (event.getType() == EventType.DAMAGED_CREATURE) { + if (event.getSourceId().equals(this.sourceId) + && ((DamagedCreatureEvent) event).isCombatDamage()) { + if (setTargetPointer) { + for (Effect effect : this.getEffects()) { + effect.setTargetPointer(new FixedTarget(event.getTargetId())); + effect.setValue("damage", event.getAmount()); + } + } + return true; + } + + } + return false; + } + + @Override + public String getRule() { + return "Whenever {this} deals combat damage to a creature, " + super.getRule(); + } + +} diff --git a/Mage/src/mage/abilities/condition/common/CardsInHandCondition.java b/Mage/src/mage/abilities/condition/common/CardsInHandCondition.java new file mode 100644 index 0000000000..fdb5aaf363 --- /dev/null +++ b/Mage/src/mage/abilities/condition/common/CardsInHandCondition.java @@ -0,0 +1,85 @@ +/* + * 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.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; + +/** + * Cards in controller hand condition. This condition can decorate other conditions + * as well as be used standalone. + * + * + * @author LevelX + */ +public class CardsInHandCondition implements Condition { + + public static enum CountType { MORE_THAN, FEWER_THAN, EQUAL_TO }; + private Condition condition; + private CountType type; + private int count; + + public CardsInHandCondition() { + this(CountType.EQUAL_TO, 0); + } + + public CardsInHandCondition (CountType type, int count ) { + this.type = type; + this.count = count; + } + + public CardsInHandCondition (CountType type, int count, Condition conditionToDecorate ) { + this(type, count); + this.condition = conditionToDecorate; + } + + @Override + public boolean apply(Game game, Ability source) { + boolean conditionApplies = false; + + switch ( this.type ) { + case FEWER_THAN: + conditionApplies = game.getPlayer(source.getControllerId()).getHand().size() < this.count; + break; + case MORE_THAN: + conditionApplies = game.getPlayer(source.getControllerId()).getHand().size() > this.count; + break; + case EQUAL_TO: + conditionApplies = game.getPlayer(source.getControllerId()).getHand().size() == this.count; + break; + } + + //If a decorated condition exists, check it as well and apply them together. + if ( this.condition != null ) { + conditionApplies = conditionApplies && this.condition.apply(game, source); + } + + return conditionApplies; + } +} diff --git a/Mage/src/mage/abilities/effects/common/SacrificeControllerEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeControllerEffect.java new file mode 100644 index 0000000000..ef495fcfca --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/SacrificeControllerEffect.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.abilities.effects.common; + +import mage.abilities.Ability; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.dynamicvalue.common.StaticValue; +import mage.filter.FilterPermanent; +import mage.game.Game; +import mage.target.targetpointer.FixedTarget; + +/** + * + * @author LevelX + */ +public class SacrificeControllerEffect extends SacrificeEffect { + + + public SacrificeControllerEffect ( FilterPermanent filter, DynamicValue count, String preText ) { + super(filter, count, preText); + } + + public SacrificeControllerEffect ( FilterPermanent filter, int count, String preText ) { + this(filter, new StaticValue(count), preText); + } + + public SacrificeControllerEffect (final SacrificeControllerEffect effect ) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + this.targetPointer = new FixedTarget(source.getControllerId()); + return super.apply(game, source); + } + + @Override + public SacrificeControllerEffect copy() { + return new SacrificeControllerEffect(this); + } +} + diff --git a/Mage/src/mage/abilities/effects/common/SkipNextPlayerUntapStepEffect.java b/Mage/src/mage/abilities/effects/common/SkipNextPlayerUntapStepEffect.java new file mode 100644 index 0000000000..619c4a0131 --- /dev/null +++ b/Mage/src/mage/abilities/effects/common/SkipNextPlayerUntapStepEffect.java @@ -0,0 +1,89 @@ +/* + * + * 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 mage.Constants; +import mage.Constants.PhaseStep; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.effects.OneShotEffect; +import mage.game.Game; +import mage.game.turn.TurnMod; +import mage.players.Player; + +/** + * + * @author LevelX + */ +public class SkipNextPlayerUntapStepEffect extends OneShotEffect { + + public SkipNextPlayerUntapStepEffect() { + super(Constants.Outcome.Detriment); + } + + public SkipNextPlayerUntapStepEffect(String text) { + this(); + staticText = text; + } + + public SkipNextPlayerUntapStepEffect(SkipNextPlayerUntapStepEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + if (targetPointer != null) { + Player player = game.getPlayer(targetPointer.getFirst(source)); + if (player != null) { + game.getState().getTurnMods().add(new TurnMod(player.getId(), PhaseStep.UNTAP)); + return true; + } + } + return false; + } + + @Override + public SkipNextPlayerUntapStepEffect copy() { + return new SkipNextPlayerUntapStepEffect(this); + } + + @Override + public String getText(Mode mode) { + StringBuilder sb = new StringBuilder(); + if (staticText.length() > 0) { + sb.append(staticText); + } + else { + sb.append("target"); + } + sb.append("player skips his or her next untap step"); + return sb.toString(); + } +} diff --git a/Mage/src/mage/abilities/effects/common/SkipNextUntapTargetEffect.java b/Mage/src/mage/abilities/effects/common/SkipNextUntapTargetEffect.java index ce9b4b953f..157250119d 100644 --- a/Mage/src/mage/abilities/effects/common/SkipNextUntapTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/SkipNextUntapTargetEffect.java @@ -54,6 +54,11 @@ public class SkipNextUntapTargetEffect extends ReplacementEffectImpl 0) + return staticText + " doesn't untap during its controller's next untap step"; + else return "Target " + mode.getTargets().get(0).getTargetName() + " doesn't untap during its controller's next untap step"; } diff --git a/Mage/src/mage/abilities/effects/common/TapTargetEffect.java b/Mage/src/mage/abilities/effects/common/TapTargetEffect.java index fa14df58e3..30acdaf498 100644 --- a/Mage/src/mage/abilities/effects/common/TapTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/TapTargetEffect.java @@ -47,6 +47,11 @@ public class TapTargetEffect extends OneShotEffect { super(Outcome.Tap); } + public TapTargetEffect(String text) { + this(); + this.staticText = text; + } + public TapTargetEffect(final TapTargetEffect effect) { super(effect); } @@ -71,12 +76,15 @@ public class TapTargetEffect extends OneShotEffect { @Override public String getText(Mode mode) { - Target target = mode.getTargets().get(0); + if (staticText.length() > 0) + return "tap " + staticText; + + Target target = mode.getTargets().get(0); if (target.getMaxNumberOfTargets() > 1) - if (target.getMaxNumberOfTargets() == target.getNumberOfTargets()) - return "tap " + target.getNumberOfTargets() + " target " + mode.getTargets().get(0).getTargetName() + "s"; - else - return "tap up to " + target.getMaxNumberOfTargets() + " target " + mode.getTargets().get(0).getTargetName() + "s"; + if (target.getMaxNumberOfTargets() == target.getNumberOfTargets()) + return "tap " + target.getNumberOfTargets() + " target " + target.getTargetName() + "s"; + else + return "tap up to " + target.getMaxNumberOfTargets() + " target " + target.getTargetName() + "s"; else return "tap target " + mode.getTargets().get(0).getTargetName(); } diff --git a/Mage/src/mage/abilities/effects/common/continious/BecomesCreatureSourceEffect.java b/Mage/src/mage/abilities/effects/common/continious/BecomesCreatureSourceEffect.java index cd5f41d547..9f01a5fd7c 100644 --- a/Mage/src/mage/abilities/effects/common/continious/BecomesCreatureSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/continious/BecomesCreatureSourceEffect.java @@ -81,7 +81,7 @@ public class BecomesCreatureSourceEffect extends ContinuousEffectImpl 0) { diff --git a/Mage/src/mage/abilities/effects/common/continious/BoostTargetEffect.java b/Mage/src/mage/abilities/effects/common/continious/BoostTargetEffect.java index 8f4d0354dd..787c4f07e2 100644 --- a/Mage/src/mage/abilities/effects/common/continious/BoostTargetEffect.java +++ b/Mage/src/mage/abilities/effects/common/continious/BoostTargetEffect.java @@ -49,7 +49,9 @@ public class BoostTargetEffect extends ContinuousEffectImpl { private DynamicValue power; private DynamicValue toughness; - + // if true, all dynamic values should be calculated once + protected boolean isLockedIn = false; + public BoostTargetEffect(int power, int toughness, Duration duration) { this(new StaticValue(power), new StaticValue(toughness), duration); } @@ -59,18 +61,41 @@ public class BoostTargetEffect extends ContinuousEffectImpl { this.power = power; this.toughness = toughness; } + /** + * @param power power value to boost + * @param toughness toughness value to boost + * @param duration how long does the effecct apply + * @param continuousCalculation true = power and toughness will be calculated continuously + * false = power and toughness will be calculated once during resolution + */ + public BoostTargetEffect(DynamicValue power, DynamicValue toughness, Duration duration, boolean isLockedIn) { + super(duration, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); + this.power = power; + this.toughness = toughness; + this.isLockedIn = isLockedIn; + } public BoostTargetEffect(final BoostTargetEffect effect) { super(effect); this.power = effect.power.clone(); this.toughness = effect.toughness.clone(); + this.isLockedIn = effect.isLockedIn; } @Override public BoostTargetEffect copy() { return new BoostTargetEffect(this); } - + + @Override + public void init(Ability source, Game game) { + super.init(source, game); + if (isLockedIn) { + power = new StaticValue(power.calculate(game, source)); + toughness = new StaticValue(toughness.calculate(game, source)); + } + } + @Override public boolean apply(Game game, Ability source) { int affectedTargets = 0; @@ -115,4 +140,8 @@ public class BoostTargetEffect extends ContinuousEffectImpl { sb.append(message); return sb.toString(); } + + public void setLockedIn(boolean isLockedIn) { + this.isLockedIn =isLockedIn; + } } diff --git a/Mage/src/mage/abilities/effects/common/continious/GainAbilityAllEffect.java b/Mage/src/mage/abilities/effects/common/continious/GainAbilityAllEffect.java index 4a35e52418..9b037a015c 100644 --- a/Mage/src/mage/abilities/effects/common/continious/GainAbilityAllEffect.java +++ b/Mage/src/mage/abilities/effects/common/continious/GainAbilityAllEffect.java @@ -37,7 +37,6 @@ import mage.abilities.effects.ContinuousEffectImpl; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.game.permanent.PermanentToken; /** * @@ -105,7 +104,12 @@ public class GainAbilityAllEffect extends ContinuousEffectImpl { private String rarity; private String cardNumber; private String artist; + private Card otherSide; public Card(Integer multiverseId) { this.multiverseId = multiverseId; } public Card(String card) { - String[] split = card.split("\\|",13); + String[] split = card.split("\\|", 13); if (split[0].length() > 0) { multiverseId = Integer.parseInt(split[0]); } @@ -160,6 +161,14 @@ public class Card implements Comparable { this.types = types; } + public Card getOtherSide() { + return otherSide; + } + + public void setOtherSide(Card otherSide) { + this.otherSide = otherSide; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -193,10 +202,15 @@ public class Card implements Comparable { sb.append(rarity != null ? rarity : "").append("|"); sb.append(cardNumber != null ? cardNumber : "").append("|"); sb.append(artist != null ? artist : ""); + + if (otherSide != null) { + sb.append("\n").append(otherSide.toString()); + } return sb.toString(); } public int compareTo(Card o) { - return this.multiverseId.compareTo(o.getMultiverseId()); + int idCompareResult = this.multiverseId.compareTo(o.getMultiverseId()); + return idCompareResult == 0 ? this.cardNumber.compareTo(o.getCardNumber()) : idCompareResult; } } diff --git a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/CardParser.java b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/CardParser.java index 2b97ead046..448c94745a 100644 --- a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/CardParser.java +++ b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/CardParser.java @@ -5,6 +5,9 @@ import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import north.gatherercrawler.util.CardsList; +import north.gatherercrawler.util.ParseQueue; +import north.gatherercrawler.util.ParsedList; import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -22,11 +25,10 @@ public class CardParser extends Thread { private boolean parseCard(Integer multiverseId) { String url = "http://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=" + multiverseId; - Card card = new Card(multiverseId); + Card card; Document doc = null; - int retries = 30; boolean done = false; - while (retries > 0 && !done) { + while (!done) { try { Connection connection = Jsoup.connect(url); connection.timeout(20000); @@ -41,113 +43,155 @@ public class CardParser extends Thread { } try { - Elements select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContentHeader_subtitleDisplay"); - String cardName = ""; - String selectorModifier = ""; + Elements select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_nameRow .value"); if (!select.isEmpty()) { - cardName = select.get(0).text().trim(); - } + card = extractCardData(doc, "", multiverseId); + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_rightCol ul li a"); - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_nameRow .value"); - if (!select.isEmpty()) { - card.setName(select.get(0).text().trim()); + // for multi-part cards + if (!select.isEmpty()) { + String href = select.attr("href"); + url = "http://gatherer.wizards.com/Pages/Card/Details.aspx" + href.substring(href.indexOf("?")); + + done = false; + while (!done) { + try { + Connection connection = Jsoup.connect(url); + connection.timeout(20000); + doc = connection.get(); + } catch (IOException ex) { + } + done = true; + } + if (!done) { + System.out.println("Card get exception: " + multiverseId); + } else { + card.setCardNumber(card.getCardNumber() + "b"); + Card cardSide = extractCardData(doc, "", multiverseId); + cardSide.setCardNumber(cardSide.getCardNumber() + "a"); + cardSide.setOtherSide(card); + card = cardSide; + } + } } else { - card.setName(cardName); - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent_ctl05_nameRow .value"); - if (!select.isEmpty() && select.get(0).text().trim().equals(cardName)) { - selectorModifier = "_ctl05"; - } else { - selectorModifier = "_ctl06"; + // for flip / double sided cards + card = extractCardData(doc, "_ctl05", multiverseId); + if (card == null) { + return false; } - } - - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_manaRow .value img"); - List manaCost = new ArrayList(); - if (!select.isEmpty()) { - for (Element element : select) { - manaCost.add(element.attr("src").replace("/Handlers/Image.ashx?size=medium&name=", "").replace("&type=symbol", "").replaceAll("\" alt=\"[\\d\\w\\s]+?\" align=\"absbottom\" />", "")); + card.setOtherSide(extractCardData(doc, "_ctl06", multiverseId)); + if (card.getOtherSide() == null) { + return false; } } - card.setManaCost(manaCost); - - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_cmcRow .value"); - if (!select.isEmpty()) { - card.setConvertedManaCost(Integer.parseInt(select.get(0).text().trim())); - } - - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_typeRow .value"); - if (!select.isEmpty()) { - card.setTypes(select.get(0).text().trim()); - } - - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_textRow .value .cardtextbox"); - List cardText = new ArrayList(); - if (!select.isEmpty()) { - for (Element element : select) { - cardText.add(element.html().trim().replace("\"[\\d\\w\\s]+?\"", "").replace("\n", "").replace(""", "\"")); - } - } - card.setCardText(cardText); - - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_FlavorText .cardtextbox"); - List flavorText = new ArrayList(); - if (!select.isEmpty()) { - for (Element element : select) { - flavorText.add(element.html().trim().replace(""", "\"").replace("", "").replace("", "")); - } - } - card.setFlavorText(flavorText); - - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_ptRow .value"); - if (!select.isEmpty()) { - card.setPowerToughness(select.get(0).text().trim()); - } - - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_currentSetSymbol a"); - if (!select.isEmpty()) { - card.setExpansion(select.get(1).text().trim()); - } - - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_rarityRow .value span"); - if (!select.isEmpty()) { - card.setRarity(select.get(0).text().trim()); - } - - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_otherSetsValue a"); - List otherSets = new ArrayList(); - if (!select.isEmpty()) { - for (Element element : select) { - otherSets.add(Integer.parseInt(element.attr("href").replace("Details.aspx?multiverseid=", ""))); - } - } -// card.setOtherSets(otherSets); - for (Integer otherSet : otherSets) { - if (!ParsedList.contains(otherSet)) { - ParseQueue.add(otherSet); - } - } - - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_numberRow .value"); - if (!select.isEmpty()) { - card.setCardNumber(select.get(0).text().trim()); - } - - select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_ArtistCredit a"); - if (!select.isEmpty()) { - card.setArtist(select.get(0).text().trim()); - } } catch (Exception e) { return false; } + + if (card == null) { + return false; + } + CardsList.add(card); + return true; + } + + private Card extractCardData(Document doc, String selectorModifier, Integer id) throws NumberFormatException { + Elements select; + + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_cardImage"); + Integer multiverseId = null; + if (!select.isEmpty()) { + Pattern pattern = Pattern.compile("(?<=multiverseid=)\\d+"); + Matcher matcher = pattern.matcher(select.get(0).attr("src")); + if (matcher.find()) { + multiverseId = Integer.parseInt(matcher.group()); + } + } + if (multiverseId == null) { + return null; + } + + Card card = new Card(multiverseId); + + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_nameRow .value"); + if (!select.isEmpty()) { + card.setName(select.get(0).text().trim()); + } + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_manaRow .value img"); + List manaCost = new ArrayList(); + if (!select.isEmpty()) { + for (Element element : select) { + manaCost.add(element.attr("src").replace("/Handlers/Image.ashx?size=medium&name=", "").replace("&type=symbol", "").replaceAll("\" alt=\"[\\d\\w\\s]+?\" align=\"absbottom\" />", "")); + } + } + card.setManaCost(manaCost); + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_cmcRow .value"); + if (!select.isEmpty()) { + card.setConvertedManaCost(Integer.parseInt(select.get(0).text().trim())); + } + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_typeRow .value"); + if (!select.isEmpty()) { + card.setTypes(select.get(0).text().trim()); + } + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_textRow .value .cardtextbox"); + List cardText = new ArrayList(); + if (!select.isEmpty()) { + for (Element element : select) { + cardText.add(element.html().trim().replace("\"[\\d\\w\\s]+?\"", "").replace("\n", "").replace(""", "\"")); + } + } + card.setCardText(cardText); + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_FlavorText .cardtextbox"); + List flavorText = new ArrayList(); + if (!select.isEmpty()) { + for (Element element : select) { + flavorText.add(element.html().trim().replace(""", "\"").replace("", "").replace("", "")); + } + } + card.setFlavorText(flavorText); + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_ptRow .value"); + if (!select.isEmpty()) { + card.setPowerToughness(select.get(0).text().trim()); + } + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_currentSetSymbol a"); + if (!select.isEmpty()) { + card.setExpansion(select.get(1).text().trim()); + } + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_rarityRow .value span"); + if (!select.isEmpty()) { + card.setRarity(select.get(0).text().trim()); + } + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_otherSetsValue a"); + List otherSets = new ArrayList(); + if (!select.isEmpty()) { + for (Element element : select) { + otherSets.add(Integer.parseInt(element.attr("href").replace("Details.aspx?multiverseid=", ""))); + } + } + // card.setOtherSets(otherSets); + for (Integer otherSet : otherSets) { + if (!ParsedList.contains(otherSet)) { + ParseQueue.add(otherSet); + } + } + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_numberRow .value"); + if (!select.isEmpty()) { + card.setCardNumber(select.get(0).text().trim()); + } + select = doc.select("#ctl00_ctl00_ctl00_MainContent_SubContent_SubContent" + selectorModifier + "_ArtistCredit a"); + if (!select.isEmpty()) { + card.setArtist(select.get(0).text().trim()); + } + if (card.getCardNumber() == null) { - url = "http://magiccards.info/query?q=" + card.getName().replace(' ', '+'); + String url = "http://magiccards.info/query?q=" + card.getName().replace(' ', '+'); try { Connection connection = Jsoup.connect(url); connection.timeout(20000); doc = connection.get(); - Elements select = doc.select("small a:contains(" + card.getExpansion() + ")"); + select = doc.select("small a:contains(" + card.getExpansion() + ")"); if (!select.isEmpty()) { Matcher matcher = patternUrl.matcher(select.get(0).attr("href")); matcher.find(); @@ -166,7 +210,7 @@ public class CardParser extends Thread { } if (card.getCardNumber() == null) { - Elements select = doc.select("p a:contains(" + card.getExpansion() + ")"); + select = doc.select("p a:contains(" + card.getExpansion() + ")"); if (!select.isEmpty()) { Matcher matcher = patternUrl.matcher(select.get(0).attr("href")); matcher.find(); @@ -183,8 +227,8 @@ public class CardParser extends Thread { System.out.println("Card number missing: " + card.getName()); } } - CardsList.add(card); - return true; + + return card; } @Override diff --git a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/Main.java b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/Main.java index 0cae3e9dd2..8d785e612a 100644 --- a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/Main.java +++ b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/Main.java @@ -1,12 +1,11 @@ package north.gatherercrawler; -import java.io.BufferedReader; -import java.io.DataInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; +import java.io.*; import java.util.ArrayList; import java.util.List; +import north.gatherercrawler.util.CardsList; +import north.gatherercrawler.util.ParseQueue; +import north.gatherercrawler.util.ParsedList; import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; diff --git a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/ThreadStarter.java b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/ThreadStarter.java index 15f728e335..3fd770e29c 100644 --- a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/ThreadStarter.java +++ b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/ThreadStarter.java @@ -2,8 +2,8 @@ package north.gatherercrawler; import java.io.BufferedWriter; import java.io.FileWriter; -import java.util.Iterator; -import java.util.List; +import java.util.*; +import north.gatherercrawler.util.CardsList; /** * @@ -13,16 +13,35 @@ public class ThreadStarter extends Thread { private static Integer threadsDone = 0; private final Integer threads = 10; + private List sortedCards; public static synchronized void threadDone() { threadsDone++; } + private void updateSortedCards() { + if (sortedCards == null) { + sortedCards = new ArrayList(); + Iterator iterator = CardsList.iterator(); + while (iterator.hasNext()) { + sortedCards.add(iterator.next()); + } + + Collections.sort(sortedCards, new Comparator() { + + public int compare(Card o1, Card o2) { + int expansionCompare = o1.getExpansion().compareTo(o2.getExpansion()); + return expansionCompare != 0 ? expansionCompare : o1.getCardNumber().compareTo(o2.getCardNumber()); + } + }); + } + } + private void writeCardsToFile() { try { FileWriter fstream = new FileWriter("cards-data.txt"); BufferedWriter out = new BufferedWriter(fstream); - Iterator iterator = CardsList.iterator(); + Iterator iterator = sortedCards.iterator(); while (iterator.hasNext()) { out.write(iterator.next().toString()); out.newLine(); @@ -37,7 +56,7 @@ public class ThreadStarter extends Thread { try { FileWriter fstream = new FileWriter("mtg-cards-data.txt"); BufferedWriter out = new BufferedWriter(fstream); - Iterator iterator = CardsList.iterator(); + Iterator iterator = sortedCards.iterator(); while (iterator.hasNext()) { Card card = iterator.next(); StringBuilder sb = new StringBuilder(); @@ -79,7 +98,7 @@ public class ThreadStarter extends Thread { } else { sb.append("||"); } - + List cardText = card.getCardText(); for (int i = 0; i < cardText.size(); i++) { sb.append(cardText.get(i)); @@ -114,6 +133,7 @@ public class ThreadStarter extends Thread { } } + updateSortedCards(); writeCardsToFile(); writeCardsToUtilFile(); } diff --git a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/CardsList.java b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/util/CardsList.java similarity index 87% rename from Utils/GathererCrawler/src/main/java/north/gatherercrawler/CardsList.java rename to Utils/GathererCrawler/src/main/java/north/gatherercrawler/util/CardsList.java index 41bf308e03..7e6ea0086a 100644 --- a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/CardsList.java +++ b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/util/CardsList.java @@ -1,7 +1,8 @@ -package north.gatherercrawler; +package north.gatherercrawler.util; import java.util.Iterator; import java.util.concurrent.ConcurrentSkipListSet; +import north.gatherercrawler.Card; /** * diff --git a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/ParseQueue.java b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/util/ParseQueue.java similarity index 94% rename from Utils/GathererCrawler/src/main/java/north/gatherercrawler/ParseQueue.java rename to Utils/GathererCrawler/src/main/java/north/gatherercrawler/util/ParseQueue.java index d99ea042c6..c45b763095 100644 --- a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/ParseQueue.java +++ b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/util/ParseQueue.java @@ -1,4 +1,4 @@ -package north.gatherercrawler; +package north.gatherercrawler.util; import java.util.concurrent.ConcurrentLinkedQueue; diff --git a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/ParsedList.java b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/util/ParsedList.java similarity index 93% rename from Utils/GathererCrawler/src/main/java/north/gatherercrawler/ParsedList.java rename to Utils/GathererCrawler/src/main/java/north/gatherercrawler/util/ParsedList.java index 2efe8253a7..caa39038d7 100644 --- a/Utils/GathererCrawler/src/main/java/north/gatherercrawler/ParsedList.java +++ b/Utils/GathererCrawler/src/main/java/north/gatherercrawler/util/ParsedList.java @@ -1,4 +1,4 @@ -package north.gatherercrawler; +package north.gatherercrawler.util; import java.util.concurrent.ConcurrentSkipListSet; diff --git a/pom.xml b/pom.xml index ed43700a5b..80d813fb7c 100644 --- a/pom.xml +++ b/pom.xml @@ -89,6 +89,16 @@ + + + + org.sqlite + sqlite + 0.5.6 + + + + 0.8.2 diff --git a/repository/install-maven.bat b/repository/install-maven.bat new file mode 100644 index 0000000000..8373a9d011 --- /dev/null +++ b/repository/install-maven.bat @@ -0,0 +1 @@ +mvn install:install-file -DgroupId=org.sqlite -DartifactId=sqlite -Dversion=0.5.6 -Dpackaging=jar -Dfile=./org/sqlite/sqlitejdbc-v056.jar \ No newline at end of file diff --git a/repository/install-maven.sh b/repository/install-maven.sh new file mode 100644 index 0000000000..8373a9d011 --- /dev/null +++ b/repository/install-maven.sh @@ -0,0 +1 @@ +mvn install:install-file -DgroupId=org.sqlite -DartifactId=sqlite -Dversion=0.5.6 -Dpackaging=jar -Dfile=./org/sqlite/sqlitejdbc-v056.jar \ No newline at end of file diff --git a/repository/org/sqlite/sqlitejdbc-v056.jar b/repository/org/sqlite/sqlitejdbc-v056.jar new file mode 100644 index 0000000000..f95d90eb07 Binary files /dev/null and b/repository/org/sqlite/sqlitejdbc-v056.jar differ