From 6ae4ac3c5e93ee4b2a1eb7be391fe87bc4081446 Mon Sep 17 00:00:00 2001 From: BetaSteward Date: Sun, 26 Dec 2010 00:34:34 -0500 Subject: [PATCH 01/23] changes to support matches --- .../mage/client/dialog/NewTableDialog.java | 19 ++-- .../main/java/mage/client/remote/Session.java | 8 +- .../java/mage/client/table/TablesPanel.java | 24 ++-- Mage.Common/src/mage/interfaces/Server.java | 8 +- Mage.Common/src/mage/view/GameTypeView.java | 4 +- .../src/mage/game/FreeForAll.java | 3 +- .../src/mage/game/FreeForAllMatch.java | 51 +++++++++ .../src/mage/game/FreeForAllType.java | 13 ++- .../src/mage/game/TwoPlayerDuel.java | 5 +- .../src/mage/game/TwoPlayerDuelType.java | 13 ++- .../src/mage/game/TwoPlayerMatch.java | 51 +++++++++ Mage.Server/config/config.xml | 4 +- Mage.Server/plugins/mage-deck-constructed.jar | Bin 3164 -> 3165 bytes Mage.Server/plugins/mage-deck-limited.jar | Bin 2389 -> 2391 bytes Mage.Server/plugins/mage-game-freeforall.jar | Bin 4366 -> 5303 bytes .../plugins/mage-game-twoplayerduel.jar | Bin 3932 -> 4838 bytes Mage.Server/plugins/mage-player-ai.jar | Bin 28910 -> 28914 bytes Mage.Server/plugins/mage-player-aiminimax.jar | Bin 36468 -> 36468 bytes Mage.Server/plugins/mage-player-human.jar | Bin 11204 -> 11206 bytes .../src/main/java/mage/server/Main.java | 6 +- .../src/main/java/mage/server/ServerImpl.java | 10 +- .../java/mage/server/game/GameFactory.java | 32 +++--- .../main/java/mage/server/game/GamesRoom.java | 4 +- .../java/mage/server/game/GamesRoomImpl.java | 6 +- .../mage/server/game/TableController.java | 71 +++++++----- .../java/mage/server/game/TableManager.java | 10 +- .../java/org/mage/test/base/MageBase.java | 17 ++- Mage/src/mage/game/Game.java | 3 +- Mage/src/mage/game/GameImpl.java | 17 ++- Mage/src/mage/game/{ => match}/Match.java | 13 ++- Mage/src/mage/game/{ => match}/MatchImpl.java | 53 ++++++--- Mage/src/mage/game/match/MatchOptions.java | 104 ++++++++++++++++++ .../mage/game/{ => match}/MatchPlayer.java | 2 +- .../{GameType.java => match/MatchType.java} | 20 +++- 34 files changed, 443 insertions(+), 128 deletions(-) create mode 100644 Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAllMatch.java create mode 100644 Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java rename Mage/src/mage/game/{ => match}/Match.java (87%) rename Mage/src/mage/game/{ => match}/MatchImpl.java (66%) create mode 100644 Mage/src/mage/game/match/MatchOptions.java rename Mage/src/mage/game/{ => match}/MatchPlayer.java (98%) rename Mage/src/mage/game/{GameType.java => match/MatchType.java} (80%) diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index b18a86b8549..09d9c8f9e55 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -53,6 +53,7 @@ import mage.client.remote.Session; import mage.client.table.TablePlayerPanel; import mage.client.util.Event; import mage.client.util.Listener; +import mage.game.match.MatchOptions; import mage.sets.Sets; import mage.util.Logging; import mage.view.GameTypeView; @@ -277,18 +278,16 @@ public class NewTableDialog extends MageDialog { private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed GameTypeView gameType = (GameTypeView) cbGameType.getSelectedItem(); - List playerTypes = new ArrayList(); - playerTypes.add("Human"); + MatchOptions options = new MatchOptions("Quick Start Game", gameType.getName()); + options.getPlayerTypes().add("Human"); for (TablePlayerPanel player: players) { - playerTypes.add(player.getPlayerType()); + options.getPlayerTypes().add(player.getPlayerType()); } - table = session.createTable( - roomId, - gameType.getName(), - (String)this.cbDeckType.getSelectedItem(), - playerTypes, - (MultiplayerAttackOption)this.cbAttackOption.getSelectedItem(), - (RangeOfInfluence)this.cbRange.getSelectedItem()); + options.setDeckType((String) this.cbDeckType.getSelectedItem()); + options.setAttackOption((MultiplayerAttackOption) this.cbAttackOption.getSelectedItem()); + options.setRange((RangeOfInfluence) this.cbRange.getSelectedItem()); + options.setWinsNeeded(1); + table = session.createTable(roomId, options); try { if (session.joinTable(roomId, table.getTableId(), this.player1Panel.getPlayerName(), Sets.loadDeck(this.player1Panel.getDeckFile()))) { for (TablePlayerPanel player: players) { diff --git a/Mage.Client/src/main/java/mage/client/remote/Session.java b/Mage.Client/src/main/java/mage/client/remote/Session.java index af7841411d8..10930130705 100644 --- a/Mage.Client/src/main/java/mage/client/remote/Session.java +++ b/Mage.Client/src/main/java/mage/client/remote/Session.java @@ -49,7 +49,9 @@ import mage.client.components.MageUI; import mage.client.game.GamePanel; import mage.client.util.Config; import mage.game.GameException; +import mage.game.match.MatchType; import mage.interfaces.MageException; +import mage.game.match.MatchOptions; import mage.interfaces.Server; import mage.interfaces.ServerState; import mage.interfaces.callback.CallbackClientDaemon; @@ -396,9 +398,9 @@ public class Session { return false; } - public TableView createTable(UUID roomId, String gameType, String deckType, List playerTypes, MultiplayerAttackOption attackOption, RangeOfInfluence range) { + public TableView createTable(UUID roomId, MatchOptions matchOptions) { try { - return server.createTable(sessionId, roomId, gameType, deckType, playerTypes, attackOption, range); + return server.createTable(sessionId, roomId, matchOptions); } catch (RemoteException ex) { handleRemoteException(ex); } catch (MageException ex) { @@ -456,7 +458,7 @@ public class Session { public boolean startGame(UUID roomId, UUID tableId) { try { - server.startGame(sessionId, roomId, tableId); + server.startMatch(sessionId, roomId, tableId); return true; } catch (RemoteException ex) { handleRemoteException(ex); diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 94f01af53ea..929c0facab9 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -37,9 +37,7 @@ package mage.client.table; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Observable; import java.util.Observer; @@ -53,8 +51,9 @@ import javax.swing.JComponent; import javax.swing.JOptionPane; import javax.swing.Timer; import javax.swing.table.AbstractTableModel; +import mage.Constants.MultiplayerAttackOption; +import mage.Constants.RangeOfInfluence; -import mage.cards.decks.DeckCardLists; import mage.client.MageFrame; import mage.client.components.MageComponents; import mage.client.dialog.JoinTableDialog; @@ -63,6 +62,7 @@ import mage.client.dialog.TableWaitingDialog; import mage.client.remote.MageRemoteException; import mage.client.remote.Session; import mage.client.util.ButtonColumn; +import mage.game.match.MatchOptions; import mage.sets.Sets; import mage.util.Logging; import mage.view.TableView; @@ -280,16 +280,14 @@ public class TablesPanel extends javax.swing.JPanel implements Observer { private void btnQuickStartActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnQuickStartActionPerformed TableView table; try { - List playerTypes = new ArrayList(); - playerTypes.add("Human"); - playerTypes.add("Computer - default"); - table = session.createTable( - roomId, - "Two Player Duel", - "Constructed", - playerTypes, - null, null - ); + MatchOptions options = new MatchOptions("1", "Two Player Duel"); + options.getPlayerTypes().add("Human"); + options.getPlayerTypes().add("Computer - default"); + options.setDeckType("Limited"); + options.setAttackOption(MultiplayerAttackOption.LEFT); + options.setRange(RangeOfInfluence.ALL); + options.setWinsNeeded(1); + table = session.createTable(roomId, options); session.joinTable( roomId, table.getTableId(), diff --git a/Mage.Common/src/mage/interfaces/Server.java b/Mage.Common/src/mage/interfaces/Server.java index 5ce8de4b8ac..18ba540d470 100644 --- a/Mage.Common/src/mage/interfaces/Server.java +++ b/Mage.Common/src/mage/interfaces/Server.java @@ -28,16 +28,14 @@ package mage.interfaces; +import mage.game.match.MatchOptions; import java.rmi.Remote; import java.rmi.RemoteException; import java.util.List; import java.util.UUID; -import mage.Constants.MultiplayerAttackOption; -import mage.Constants.RangeOfInfluence; import mage.cards.decks.DeckCardLists; import mage.game.GameException; import mage.interfaces.callback.CallbackServer; -import mage.view.GameTypeView; import mage.view.TableView; import mage.view.GameView; @@ -54,7 +52,7 @@ public interface Server extends Remote, CallbackServer { public ServerState getServerState() throws RemoteException, MageException; //table methods - public TableView createTable(UUID sessionId, UUID roomId, String gameType, String deckType, List playerTypes, MultiplayerAttackOption attackOption, RangeOfInfluence range) throws RemoteException, MageException; + public TableView createTable(UUID sessionId, UUID roomId, MatchOptions matchOptions) throws RemoteException, MageException; public boolean joinTable(UUID sessionId, UUID roomId, UUID tableId, String name, DeckCardLists deckList) throws RemoteException, MageException, GameException; public boolean watchTable(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException; public boolean replayTable(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException; @@ -77,7 +75,7 @@ public interface Server extends Remote, CallbackServer { public UUID getMainRoomId() throws RemoteException, MageException; //game methods - public void startGame(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException; + public void startMatch(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException; public void joinGame(UUID gameId, UUID sessionId) throws RemoteException, MageException; public void watchGame(UUID gameId, UUID sessionId) throws RemoteException, MageException; public void stopWatching(UUID gameId, UUID sessionId) throws RemoteException, MageException; diff --git a/Mage.Common/src/mage/view/GameTypeView.java b/Mage.Common/src/mage/view/GameTypeView.java index 97426e47f9d..310e4057271 100644 --- a/Mage.Common/src/mage/view/GameTypeView.java +++ b/Mage.Common/src/mage/view/GameTypeView.java @@ -29,7 +29,7 @@ package mage.view; import java.io.Serializable; -import mage.game.GameType; +import mage.game.match.MatchType; /** * @@ -45,7 +45,7 @@ public class GameTypeView implements Serializable { private boolean useRange; private boolean useAttackOption; - public GameTypeView(GameType gameType) { + public GameTypeView(MatchType gameType) { this.name = gameType.getName(); this.minPlayers = gameType.getMinPlayers(); this.maxPlayers = gameType.getMaxPlayers(); diff --git a/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java b/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java index 478dcb5b933..97c25e4c5fd 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java +++ b/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAll.java @@ -28,6 +28,7 @@ package mage.game; +import mage.game.match.MatchType; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -59,7 +60,7 @@ public class FreeForAll extends GameImpl { } @Override - public GameType getGameType() { + public MatchType getGameType() { return new FreeForAllType(); } diff --git a/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAllMatch.java b/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAllMatch.java new file mode 100644 index 00000000000..613fcf6af78 --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAllMatch.java @@ -0,0 +1,51 @@ +/* + * 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.game; + +import mage.game.match.MatchImpl; +import mage.game.match.MatchOptions; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class FreeForAllMatch extends MatchImpl { + + public FreeForAllMatch(MatchOptions options) { + super(options); + } + + @Override + public void startGame() throws GameException { + FreeForAll game = new FreeForAll(options.getAttackOption(), options.getRange()); + initGame(game); + games.add(game); + } + +} diff --git a/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAllType.java b/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAllType.java index ee24d0675a2..a7fabda1722 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAllType.java +++ b/Mage.Server.Plugins/Mage.Game.FreeForAll/src/mage/game/FreeForAllType.java @@ -28,11 +28,13 @@ package mage.game; +import mage.game.match.MatchType; + /** * * @author BetaSteward_at_googlemail.com */ -public class FreeForAllType extends GameType { +public class FreeForAllType extends MatchType { public FreeForAllType() { this.name = "Free For All"; @@ -42,4 +44,13 @@ public class FreeForAllType extends GameType { this.useAttackOption = true; this.useRange = true; } + + protected FreeForAllType(final FreeForAllType matchType) { + super(matchType); + } + + @Override + public FreeForAllType copy() { + return new FreeForAllType(this); + } } diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java index fe3b9d76fe4..7c53f6eab1b 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java @@ -28,6 +28,7 @@ package mage.game; +import mage.game.match.MatchType; import java.util.HashSet; import java.util.Set; import java.util.UUID; @@ -39,7 +40,7 @@ import mage.game.turn.TurnMod; public class TwoPlayerDuel extends GameImpl { public TwoPlayerDuel(MultiplayerAttackOption attackOption, RangeOfInfluence range) { - super(MultiplayerAttackOption.LEFT, RangeOfInfluence.ALL); + super(attackOption, range); } public TwoPlayerDuel(final TwoPlayerDuel game) { @@ -47,7 +48,7 @@ public class TwoPlayerDuel extends GameImpl { } @Override - public GameType getGameType() { + public MatchType getGameType() { return new TwoPlayerDuelType(); } diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuelType.java b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuelType.java index c25aaa8dc2c..c15c12f1e4e 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuelType.java +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuelType.java @@ -28,11 +28,13 @@ package mage.game; +import mage.game.match.MatchType; + /** * * @author BetaSteward_at_googlemail.com */ -public class TwoPlayerDuelType extends GameType { +public class TwoPlayerDuelType extends MatchType { public TwoPlayerDuelType() { this.name = "Two Player Duel"; @@ -43,4 +45,13 @@ public class TwoPlayerDuelType extends GameType { this.useRange = false; } + protected TwoPlayerDuelType(final TwoPlayerDuelType matchType) { + super(matchType); + } + + @Override + public TwoPlayerDuelType copy() { + return new TwoPlayerDuelType(this); + } + } diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java new file mode 100644 index 00000000000..c3f7723146c --- /dev/null +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerMatch.java @@ -0,0 +1,51 @@ +/* + * 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.game; + +import mage.game.match.MatchImpl; +import mage.game.match.MatchOptions; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class TwoPlayerMatch extends MatchImpl { + + public TwoPlayerMatch(MatchOptions options) { + super(options); + } + + @Override + public void startGame() throws GameException { + TwoPlayerDuel game = new TwoPlayerDuel(options.getAttackOption(), options.getRange()); + initGame(game); + games.add(game); + } + +} diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 9733c9db7e0..df6998b9b93 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -9,8 +9,8 @@ - - + + diff --git a/Mage.Server/plugins/mage-deck-constructed.jar b/Mage.Server/plugins/mage-deck-constructed.jar index cdcc7affaa240bc8c3af37058da4f7cf4db3501c..5ce9f4b46939206ea0c790644abc405b5a2e886e 100644 GIT binary patch delta 869 zcmca3aaV#jz?+$civa|Bx6Yi%tHRs^q)etTgLtPEz|=cc5Vcv1aUKhZF?lAFJ8#qom?!cGF61%p|nDad!60dcU#wOlbHU5L1tptg=z<2+q1Rr||`z4CDEVSGhDz;B>N`~4G z)}n_(^@o`*K76IOulu5B>gp>VGvX!GmY!JfgEvUB^GcZK2ZrpKq7x64W{HcPSh`~M z`p7Ji0M8j!bxAzyV4z~N<&v~@3%0O>#wpToJ zZI2yy@}e~>+j6v$qZah5q}%w*cjRQvo1Qm&vQW<24J+$K`Lb^AebB3%7QlGEA((B| zbw_Ud(gz1tZvS7?G{Jpdy3OqDynCA(b7OP+ZTf9W?E2o{aLzH_cX+?4_OJRrp{+{_ z&MpxY_@o&+Nv(pX?8~YZz3a0|LfiQcdQPgziEXQ$%Q}4-XLgdGYve|zlqY=p(dHZS zlNaruS-3_u=-_vq`t?g?kG-rrzqF*+ZqC_Ag)F7w3wM49r(s;V;yd5eB zV*_H-F00<1&C$7L=Ca%I@r(L7^RBPY)s=8wW&U66zOi3d{@ILM*HyFUG2D{B-S^<~ zJ+|BXIf6nGq}W?8A2_i?(5v}BECq3ZQjo25`mXK8z|_Q8K6yKbb3O0bja3Rik(5xxOT(!T`{wn zbYFDvccYEpY37rqd}lOwzN4Z-)a9Y?$`;=)#aW+FMM&=IL9$ zb?pxbPxu$$&B!Fe3`x`A^jX7c0!r1BH*?y9`CmB=z_bRJDVR>+as$&VxvaqSGcIQ^ rt-~FOmaZpphbbU?vK;798&KYXf+dX$IVSJnu$%10Bfyr&2@(YWE!TH~ delta 867 zcmcaBaYuqTz?+$civa{|Ha1V>RbjRUQYKTFLA=unVCtPJh}ta1IFALyn7o};3rs&@ zH3rjCY@WOo%bRUa$bLHUg^_{b)#L&;@A_qTH4c70eQLploaVKu`L2`mW43NfQL*%t z{QG{g>e2ghi4)jgzjz^ccXF|)%2bi_4y6@3-0SSt&fBuiOk#Qpzs$t03+fI|&wl*g zuDj34uq(W8r=`}lP5kZ5Rxwfctormntbco#zj%X+;A1agzeLfNg_auw#r6qL$x!4^mu3@dwFvR129G;BT7wI(pet8% zr%ADfOp+0*akAo@IO{FPRkj?Bi6TzwJKD>x>=p01*v|6ax^#2v?^_3VeAuR0!~8|{ zmrhUXre#u_R(51I?3*y>qfM>arj?aZyK?=!Hf%k#EX<;QMe6sa^1d04tTxPkk}K~0 zjsCHfncZ&h|Hq%3Z9bVjz7bRWxsBK7byoG0+$Re^7uFn?OPc*r_TRe5i~qBytWa&0 zR_kq;uzuwvlY_|{7Kgj$)`TuzZB@eISMFwMb?e`1_lz?paMV_5q3`|U=lXq}9*9VvRpY_)D((&{S z^jH=0!B@xo?Bl0FW!k4g3Os$o3=F~yj5Zl<*kWWFSis8{XkcVu^5ChKx31Q?Gv_y( zUNC<8)Jy-AzU~K~jc4_}uAlbx@dt|NXr1)&_R(YH`o+Xr(#V$j+Uogp(di;n)tGf3 zJ!N7D@MdHZVTPn=aO$k(Gy$dQ$y+$>!TfKW24Gr~%M?r}a=C%&Ra{nJ`Z<>~nAYWv mL`&6^xx*At{0Pc4pqv8*OBxq&Oy0|3H`$#>fGw93BnkkyMs%eB diff --git a/Mage.Server/plugins/mage-deck-limited.jar b/Mage.Server/plugins/mage-deck-limited.jar index 8e20dbdb8ac636efff605c76d71c47303fc7b641..57ea157b7a8aed85523de920e5a0d7a6634c18b3 100644 GIT binary patch delta 961 zcmcaAbX|xyz?+$civa|Bx6Yi%tHRr}b*8QQ^K%wf1b{VREGc7Fz>blm2dzqam zFt%;3Pfi_>F3)pmVoN*^3h%xyxlNN}c#B2=anlZZyN^Y5ID?2+U z@e?Bh!vj_Z2E)mG%+iw&a7fnA4e{;1Z6IQEegC1BI9Gx`PlUforH z9$j6hE9vRCY0BUCQ&o@Nk2}=Fe=XySTfWVC#~{y-3?c2$H$8ZD`BmKOtnNb3z5{|Q zIrg%vr1b5tzx`NRWO2pQ8Mikl>HaRUT6o#|^z9=brT*W1UpJ#!wNoN|Rsq*)1)ln| zoohFJ@C#Tp^FjEDR&O3|m*cZ{|Il+wpSm_kIdPtWkJm)UkJ3wwgjTMd^nmf2k+xFf z>nnOX6TAb%<2PN=STO0yga_Q03Ola6aaduN;M}=H!0Mpwmc?>=6MF6~E^X?cyYJ?a z+OG*QRrjatWBn!ZHsYj7N-$?}?Jtj_W1qh?o~U1TFZJa%OU;AVIA>cWMjwB4P}=>} z0;YM4%XvcHKhynlrIES)-oMW$9px(Leau?7vT`+BS?N~WPd1;<{WRZ~%)jyM55E7` z*1V_}QO)vvIL%X8;Ysi+72gjMZ(f83O8Z}Zv5HT&X)?>t9lPHAvf|eBoqYAk?5<5o ztS3&$#%;?>xL<$FrOxz2!Z=Xt-Bs@*YTv%?jaXA5L6?)Mk{$URAI zhjE&owN0UC)9wYk&MnK{WiBWbY3#YbuCC8cZAa|>ZCev~R^Gc4{pWy%=03Bibr01Lb;D;q64hBJBLa{w*e`i-AFzFQou>~|` zh)w>>svyGWrxRG<=^JKX6l!3!$;fb{kx}4eLpD+7tHw_zyR#|DxTU`SmGUf&Y3;+O zOu*F1$RxrHPuY`O*-TKA_T*!1rrA9&0>r@89|K6mzcD`^nWH}Fm1~0DkKuuZ2Ru{ z^OGML85r)dGB6l2Ob+1CsV~US)vL(OnHv(DecM2!c3yoeXZ6-6%kD}X{2VOFGb!&v z?whq4pL$nco7SVEuDSGo?WQF^ZY%TrNsyJjySTb$UGtJjk2qKG`KKPd`uytRYggGG zPm*a+4ifyu;c-Hy{{Oba<{EAvPZ^r4AC3Cb zuGI=WXFGd0eFzI!H1mP@iB@kOZkP7i?LQRV)TgcuQcj#_;NvyX@gw&VBcYXRCp}=i zW~8mu`1*>T&IIqk@c7MFG!{&HGU0*orNWLYIsPlm5}Z4i2v{Ao-LhD2PePBK%8v^x zXY1a%&UdV}-1lMhg7X)rP4#g$6E&E={Jctix{d$*j+|F>r{~R{ER-{M!%E?8r8;tZ zikI+cJ&-IoUy39;vv&-mUwPk4TGXE}i-`KBfcbdst zd(~{W{TBT>+h0z;*LLea=aLl%OnAALH%=^gJejSY5tNi9K*^8;lniZ?cpfgy2l5Jm z*aDn5CjVqr5h>F?6;j~o8)je-W?-_#XzKKAJ9Dpg0^r%mQ%*7~VQQ zn(WUm2~N@3?9z-}liS(Vz`PCYk}Pi>k1>*#fk6NO diff --git a/Mage.Server/plugins/mage-game-freeforall.jar b/Mage.Server/plugins/mage-game-freeforall.jar index 25f863f3dfde0d688e99b3635646e563530c6eb8..170e2d6993bd2e0a4bc64348d9799d1f7696a83a 100644 GIT binary patch delta 4194 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 G<9`4GO|cRH delta 3346 zcmZ`*2T&7O6AnEBp*IO#%0;R)flx$Ifgm77kR~nkCIm5#^kPfc>d92_>)idB75;Wq z*VQo*O-Yn7U;PosYGu8XG8+sSj9$!Oz5XZF7;1ZgE=D{> zy-c^dz((FtbJy`{srz>HJ#@e;liXG++Rv5QUN3m*>6cjXoM&B^iT)QGGDTrN@FLMf z-(~oS#%t%_e#e%zr(TWsj-cj8CY*z^_nhfIXp8mSAFGb&XQfAmFDiH$&StF4B%2z_p-6@tG51Q!7UnSjlg(Qtv=E+ z%^R9+W*I&GC=OT`C?Tsuaf6OvuRgAqSo?kZ#@z@9an_R65*7~F+$%hOrYLu=`MY`D zYC*{DO;fEe@xd?Z_V5b>-Hi(IJN2r=F+-$RY$spXa)rF)fANT$Xgg?^p%GvK8Kzix z8Nzp@N0xg9T_=u_<7 z^)Ow*dH9v+S20Vwch@Sb%0dObhoF0&Qu4-G!Hea-!y8ImCS>O~=sm`X>&L_Qf?;1WSecRu;U|`r&!tKeXvvvOXX1Z z?z^p}D|~l&3)2k6T9x%dY)&~Xm(MI4rkjmqH#uKVefY7*ZXTZU@l=^caU}>bplg=) zCVfkg)IieCJJ$RBG@1-qk=+Q#@gTv!VzIqHE+Ni`ZESDSm8Y>Kugg44TKeYQUEcKi zao-#>d9RtDb?U|ZPx;v!V94bq-(a6G=m{^e+Sk5ecqj(?dLps&hCoVyP9D9w1!`e8 zX&EbHL9H<7oFBd2DiGD|q-f(p4z*8DJ0p)EemCw-{B^u63q90|pDNdc^bbIy2eRiX zc}`lDl-cOjK9Hl(GRle+ig5NqtW3pD>DLpP7q-frq^3Qj$m5tImQO#-)QCu0h7V^x>oqvxLsUP%pefqP|I3E~$*>4UmvLLM;bEbCnn*UWo zPPjpl#ba+WTIl#9WMf&A9C0Oa)noM|>9fp^h}VOQ_L6e+7lFE#r79fuYqkSvlNLE9 zXLMoMrR&_!zI$dQH4Tr0$E8n+q(lfbvpr$QoRZg>%mP4i@wJEe>5zOn4=*V8VPE3|tp@I$EvIpAoOoX)BWkMMlipJxwAB>w zT>F?s;gwe~kJ>3@!hIT?;n%h;P2**_ojru%hf0UmsDd%E{uc1z32&I4Y+cxjO8`@s z&lWWD@&_~K@t8tC_hJ_MqDb%+%X;E9$Q<1Y&I;B%-H+{z%r(v0v`z5Z)kzlv%XCY; z>wA|(=lE;b0uP6+LEM%!DS!b0;AI2=BmlTCd{XGhd#*4>V}Yaza$iPpPnA-&YIX03 zOddt%UJI~5ragrw4-N`9@^;O?bwwEV;k?mPNx?tzFC)k<`gqjD&r`mJh7;oqV$VE@ zq*(R_&5w@mO6serau+a_c#=6^DPywN^1_OV6rq|Tq-MDXmAmKfXXVN{<}6r4eRa;f z?khUF;M0A~>`uL3!e)$=wcqQi(h}E&k^lqi5*nMtJ4kh)?+f;6u4NQ z1$CK*XQ}_1Q0T$FwIxb%50c3ZjS6!o?<&ekVvZw}IP#2MN~9KRCuAeI89!mG*jj96 zs5|xI4IRAcSCk_n2AaJjx!7Wn)5yi&8fD%_pEg=*%}R3dJI4}lA9v575>4hJWTuL$ zw6n~7&`n|@?yb8bgumunIwUK5pn>hrE%LTl%qe`yw#i+<#T-FuSp0i1=QKx zRKhk8-avQGMX0P&GfOU@X*EIhiGpD^MVV*Q1ge}mlN*!4#CHec zPh`05uMf9MpxnX&iyPq87n0UTBv6mHhGKNyOard>ZE<}MbmQC?QVwjNE2JEd(7{R%u>Uzc}HRcDYl zSKxSm;7mst!6*#UK>Thwiu$a9hI!|76U6HgUF9SHks!;z8izcwlIQu>G*fmw3qWlR5$;rfh{9Y-KSZ zhD+3k?0Y-EIvhY*CJmjb<&x>mFh*Nc8uIsJ3kic!2pYbPGM#+iiLpB6mZznG7MI0` z-7#9k6D>_eHikB*sYiw3RHTW)%E?*>-Y4r-O73)P7z3jK)8D-P&*}vLaOS*9hXfvn z;x#&|-zHwQBT3`c`Y#Tz#s`)9-%seM3yzKgOaSoUOHy(@RQl%{0O0-`@BsGrc@AQ@ zbl%Qb+062SYOqHXJVf>X#CBvR@-JfpcAN*l{84~delTOiAr!?~aS1B@;{yozSO2j9 z0GfaM000Uwgujf$a5@5){i}!IowM0ER$^ F=s&R-63YMp diff --git a/Mage.Server/plugins/mage-game-twoplayerduel.jar b/Mage.Server/plugins/mage-game-twoplayerduel.jar index 22b21ec61e3cfc6bb084f72f1a1acdad6247ace3..82888f63b1ab46ce292fc87bc305ef52569adc05 100644 GIT binary patch delta 3734 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 Yofk=B;8$p=C_$8<;PZXw{;$6O4`i{amjD0& delta 2840 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 D*WA%7 delta 1072 zcmezLkn!C^M&1B#W)?065b)gFF_Bk=*#k(KOkqym)iLq30+@QI4x%PYFlvEmS4LA1 zy}6e05tAU0Ve2866C=XHz!1;Dz@RdDBe&>eu^gez(^=$Ln1N<)-of#L3Cvi`y&J+v zC|q0l5;so z>}r5UtHUBm{3an}-`SmxN zMg1hMvlTnlhX=~s7M6bdAlh+5VpF_=xW1vxgg5KuFSU!vl`Jc{cHE@ZG95lXExWOo=y>nxNv*#26wu5rF0>3*N zu)Y_(Q~s)qn{Vsl!k4+hzt$a5Up48x`Q=~R%0G#V_lVyV4y*Uw`T0_QaZF&i$~V_X z%Q~1Bo6nmV>{;n&nHciDD(_)1JKvtl-})(REKe$rE!|o8%vVa{>Q%lIpHG;-zq{kO z+@m&5;eRV5Z~m8wy3%z)-zSoBq2J6fmV<^jE~Z9CJAYjf+Rt~;v&<==Hp_V~>-1$a zvzmNeR3kI@@3r5PbN9f$h^@T8tB!4)=D~7*!pT2IDB&s>!O3J790~k z{5wQ&pF(D^b@H=KhlQt$eC2(0I%ii(m9*kS^Xo7EJ-_}$x#{h%t&3fgHCR?1F7J8# zDZ1G7?thob4w*7C%&_Fk!2n8$LFx?JIl!cx2gIh(6d*f!L#B8Ndr@rAWKkX+k)EEl zJi2Rnd_-fKokeHbNs;adbz=1x+OOVOyA6n0@G%B z@nCvJ9)$ljF9xhXB;N{5Pt11))7SHZcu_LQWU~T)1&ja#6#`JOq%kIQ^16H}U=Gq{ JlgomK!-@6*Xci<37HJDMWCb&J zMSS1_Gu9?YKuqOG+r`8OGF3D$X{wMg14FC!WJfO1$?}ctlY00z|H_aN25XS4dCUT4 z{BM{I);W1@GuVNXPc<8ZBsR4yX5s)DWIW?fm-%|T~rT3$+PV*cmKKZcFaQ@448uN{1b)If5S8QvoTktQyn~_O`86K{- z-~gUn)2;^!-pOm*?LiKl{Jz})Oe=MmgXx1kl9Nk26v6D79iCwNX-6QKw(Nw^y`BDG z{(UIlxC_E>?Q#Q)pX&+&)5_h!V7js!B7Yi6%l7zz#q)Xs!1Tc$D=_`9#~Dn!^zK0f I8b@Ca0G|-=tN;K2 delta 551 zcmew|hv~~4Cf)#VW)?065LmWr>O@`@=A}T&WC}BgcUl2Vy;BEKlO-6nK=fu;#z#zG zh8D|FCSIV3t^KbkSq5eXhW^R?#wwc?*oB$F@@8CTG{6j2V@-&Np;?eDSfnl3kQL0> z74d-!%vhTo0Wp;$Z5IlO4H4C(AdoPwL^@{3}C77_33E<}nMH z@xNg?f~o3#y+`Z&6_OBxn~0U#nLkhsZ1b zn6FYyrkd#@e}Dfv7H(^?G2kKRJ@$%Q7MY>E+K1=57jV40WRWAVu$uRQPSjenX{yhz zL=~H^2%WI!->;Qth0gdiZdKC!=$veMd48b%{*nz>w%B?@1{AnmH+v5io&+7>Q(+7L3!1TW!XE5#3y9W_y H9DO+ee<|<3 diff --git a/Mage.Server/plugins/mage-player-human.jar b/Mage.Server/plugins/mage-player-human.jar index 37aa8262f09843280fa8ac4f6288081e9856c8dc..2f32a784d2ba60ce304bfa9bc69f0e64506ecfde 100644 GIT binary patch delta 941 zcmX>Sek`0fz?+$civa}sw$7Z$tHRt1q)etTgLtPEz|=c+5H(qXQ5QsS_F(j60yCyC ztH^;FyA`K_MJ9)+Xn~bBsu+Xm?JBN<=3A!PI{%P6_?nS{;T9_cgW=?U73s+jRD>ok z&}Nxzrz%*#Jk@apm!%@hC9$w3*E?ayDt-5=Y)iHNEAVLl;~RN)(jkgZ1^SQY|55ua zv^1zQeSw-?sOt6iV%5n{*dBMuG$<=^-eneAb$~y9+YdffjmU~;&z3}C$E|Mp;)j^y>W0Wxp(s>EoYfqvhT#2 zmfA-aYhC!BmIvJON?GUC#U62biVLIp%xH@R0gI;1|8z!cL6^vcN8FbRJB)JNS8Pmh zj?_~0>8j3fw`DoVve#|3h`d$p?#}jC7kB*Frui>mlJSbC$9E~!XeCK6*za+$Vxq0^ z)sQ_pf6dfIcUWy)FWhcb&(|MU5yU4{!8ga~!rtB6KkQ`A|N0_I`?{{wjW3_dJ(Rgl z%%7NkZ~MoyRUO%iU%qZmOZ}gm5va}ee&!}7r@6*!GrEo^xKB^boqJ{R>Z%eBzZg!# zuQPc|(_&_+t?D!totm=r@4J5=_LMUJzGn97wRYcD7p)(CqFaj&o8NuEbIRKK3$wc| zja{5W7>@}b{v9It-}%z9uU#`g^ZCaFJZQ_@}Jrz{y+Elv$ji4y4PlPX1|b7&gBJ#ldH8Kv1@eyV}d0y4p0)a z74m<+y8xKnih@4Fni2*iNXoC@?$XY|W7;_EtgU4S&p6 z9w*-?Jx%rZ*W_139-r5$bpFHM#0A}Z!)|&AM$Kc;c35+I(=m3NWek7bXz?+$civa{S?QNaNtHQhyNSRDw2JucSfT?%tAZoG%qb`Wv?7`^C1ZGTO zR*?fUb}LQ;i%bqt(E=-NR51qA+f`fztClz0UXj#~dBw=UaD$bB!EkcFiuB|O+G3OK zR0ZqPPd5dK7CCV&(OtvRy<=_9=UG*rIj75hDITdmzVU92+3KvNGLJs~=)Ip3wT|$5%c1bvP-#Cnh;&qHTeT%0g=gC($4OZnNEA#G=Z5K1SX&`)2!LM%jB7_q06o zKCC}`x4e3TlJ~K#igOMrbtP9Pt(C1&*D&-KGK+X_3uWot2$<%@ zG5L|)t-$Y&29Ei`d){ApD<+ra{`k`DWnXGN{T57{|GMkL%9g!$f1SCGb-(FcvFuUh z%k7pc4_xi6oojKWu)gBUsmVnX4irC_()jM~o>qU^inY$uqju(8II!e;y|Y-L!@pje z&-;4wuQ|tXFI<1$RQvaQr|zi0A19`W3cQ+<=^AXqX1z-%`}GpJ*sF`33m$oKtn|-W zVf^@#iNEiPl`B-V-%`B@DX(}miK*VPcCPRBl6+;APib{>bv04@{0n#2WoBQL;A(vJ zLHF^JKeu?6{GRS%|H0aR#oaWID(P1S|Ghjm_%sx|00l=A}4fX54zWaCg|@?0-&+vs90I*61AkzUoqt z1z)CouKbNqX{$+0mU{IjhL5K#{IjM)aJus?gZt(I-i%Bl% playerTypes, MultiplayerAttackOption attackOption, RangeOfInfluence range) throws MageException { + public TableView createTable(UUID sessionId, UUID roomId, MatchOptions options) throws MageException { try { - TableView table = GamesRoomManager.getInstance().getRoom(roomId).createTable(sessionId, gameType, deckType, playerTypes, attackOption, range); + TableView table = GamesRoomManager.getInstance().getRoom(roomId).createTable(sessionId, options); logger.info("Table " + table.getTableId() + " created"); return table; } @@ -202,13 +204,13 @@ public class ServerImpl extends RemoteServer implements Server { } @Override - public void startGame(final UUID sessionId, final UUID roomId, final UUID tableId) throws MageException { + public void startMatch(final UUID sessionId, final UUID roomId, final UUID tableId) throws MageException { try { rmiExecutor.execute( new Runnable() { @Override public void run() { - TableManager.getInstance().startGame(sessionId, roomId, tableId); + TableManager.getInstance().startMatch(sessionId, roomId, tableId); } } ); diff --git a/Mage.Server/src/main/java/mage/server/game/GameFactory.java b/Mage.Server/src/main/java/mage/server/game/GameFactory.java index 6af3d534bd4..e35ee4b9ca2 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameFactory.java +++ b/Mage.Server/src/main/java/mage/server/game/GameFactory.java @@ -30,17 +30,15 @@ package mage.server.game; import java.lang.reflect.Constructor; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; -import mage.Constants.MultiplayerAttackOption; -import mage.Constants.RangeOfInfluence; -import mage.game.Game; +import mage.game.match.Match; +import mage.game.match.MatchOptions; import mage.util.Logging; -import mage.game.GameType; +import mage.game.match.MatchType; import mage.view.GameTypeView; /** @@ -52,8 +50,8 @@ public class GameFactory { private final static GameFactory INSTANCE = new GameFactory(); private final static Logger logger = Logging.getLogger(GameFactory.class.getName()); - private Map> games = new HashMap>(); - private Map gameTypes = new HashMap(); + private Map> games = new HashMap>(); + private Map gameTypes = new HashMap(); private List gameTypeViews = new ArrayList(); @@ -63,31 +61,31 @@ public class GameFactory { private GameFactory() {} - public Game createGame(String gameType, MultiplayerAttackOption attackOption, RangeOfInfluence range) { + public Match createMatch(String gameType, MatchOptions options) { - Game game; - Constructor con; + Match match; + Constructor con; try { - con = games.get(gameType).getConstructor(new Class[]{MultiplayerAttackOption.class, RangeOfInfluence.class}); - game = con.newInstance(new Object[] {attackOption, range}); + con = games.get(gameType).getConstructor(new Class[]{MatchOptions.class}); + match = con.newInstance(new Object[] {options}); } catch (Exception ex) { logger.log(Level.SEVERE, null, ex); return null; } - logger.info("Game created: " + game.getId().toString()); + logger.info("Game created: " + gameType); // + game.getId().toString()); - return game; + return match; } public List getGameTypes() { return gameTypeViews; } - public void addGameType(String name, GameType gameType, Class game) { + public void addGameType(String name, MatchType matchType, Class game) { if (game != null) { this.games.put(name, game); - this.gameTypes.put(name, gameType); - this.gameTypeViews.add(new GameTypeView(gameType)); + this.gameTypes.put(name, matchType); + this.gameTypeViews.add(new GameTypeView(matchType)); } } diff --git a/Mage.Server/src/main/java/mage/server/game/GamesRoom.java b/Mage.Server/src/main/java/mage/server/game/GamesRoom.java index 3e72cdaa061..901a6f12fb9 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoom.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoom.java @@ -34,6 +34,8 @@ import mage.Constants.MultiplayerAttackOption; import mage.Constants.RangeOfInfluence; import mage.cards.decks.DeckCardLists; import mage.game.GameException; +import mage.game.match.MatchType; +import mage.game.match.MatchOptions; import mage.view.TableView; /** @@ -44,7 +46,7 @@ public interface GamesRoom extends Room { public List getTables(); public boolean joinTable(UUID sessionId, UUID tableId, String name, DeckCardLists deckList) throws GameException; - public TableView createTable(UUID sessionId, String gameType, String deckType, List playerTypes, MultiplayerAttackOption attackOption, RangeOfInfluence range); + public TableView createTable(UUID sessionId, MatchOptions options); public void removeTable(UUID sessionId, UUID tableId); public TableView getTable(UUID tableId); public void leaveTable(UUID sessionId, UUID tableId); diff --git a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java index a4fc250fa50..f327c233065 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java @@ -39,6 +39,8 @@ import mage.Constants.MultiplayerAttackOption; import mage.Constants.RangeOfInfluence; import mage.cards.decks.DeckCardLists; import mage.game.GameException; +import mage.game.match.MatchType; +import mage.game.match.MatchOptions; import mage.util.Logging; import mage.view.TableView; @@ -71,8 +73,8 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable { } @Override - public TableView createTable(UUID sessionId, String gameType, String deckType, List playerTypes, MultiplayerAttackOption attackOption, RangeOfInfluence range) { - Table table = TableManager.getInstance().createTable(sessionId, gameType, deckType, playerTypes, attackOption, range); + public TableView createTable(UUID sessionId, MatchOptions options) { + Table table = TableManager.getInstance().createTable(sessionId, options); tables.put(table.getId(), table); return new TableView(table); } diff --git a/Mage.Server/src/main/java/mage/server/game/TableController.java b/Mage.Server/src/main/java/mage/server/game/TableController.java index 31ef031c3b0..48da85e1ee2 100644 --- a/Mage.Server/src/main/java/mage/server/game/TableController.java +++ b/Mage.Server/src/main/java/mage/server/game/TableController.java @@ -55,7 +55,10 @@ import mage.cards.decks.DeckCardLists; import mage.game.Game; import mage.game.GameException; import mage.game.GameStates; +import mage.game.match.Match; +import mage.game.match.MatchType; import mage.game.Seat; +import mage.game.match.MatchOptions; import mage.players.Player; import mage.server.ChatManager; import mage.server.Main; @@ -73,17 +76,19 @@ public class TableController { private UUID sessionId; private UUID chatId; - private UUID gameId; + //private UUID gameId; private Table table; - private Game game; + private Match match; + private MatchOptions options; private ConcurrentHashMap sessionPlayerMap = new ConcurrentHashMap(); - public TableController(UUID sessionId, String gameType, String deckType, List playerTypes, MultiplayerAttackOption attackOption, RangeOfInfluence range) { + public TableController(UUID sessionId, MatchOptions options) { this.sessionId = sessionId; chatId = ChatManager.getInstance().createChatSession(); - game = GameFactory.getInstance().createGame(gameType, attackOption, range); - gameId = game.getId(); - table = new Table(gameType, DeckValidatorFactory.getInstance().createDeckValidator(deckType), playerTypes); + this.options = options; + match = GameFactory.getInstance().createMatch(options.getGameType(), options); + //gameId = game.getId(); + table = new Table(options.getGameType(), DeckValidatorFactory.getInstance().createDeckValidator(options.getDeckType()), options.getPlayerTypes()); } public synchronized boolean joinTable(UUID sessionId, String name, DeckCardLists deckList) throws GameException { @@ -100,8 +105,7 @@ public class TableController { } Player player = createPlayer(name, deck, seat.getPlayerType()); - game.loadCards(deck.getCards(), player.getId()); - game.loadCards(deck.getSideboard(), player.getId()); + match.addPlayer(player, deck); table.joinTable(player, seat); logger.info("player joined " + player.getId()); //only add human players to sessionPlayerMap @@ -116,7 +120,7 @@ public class TableController { if (table.getState() != TableState.DUELING) { return false; } - SessionManager.getInstance().getSession(sessionId).watchGame(game.getId()); + SessionManager.getInstance().getSession(sessionId).watchGame(match.getGame().getId()); return true; } @@ -141,7 +145,7 @@ public class TableController { } private Player createPlayer(String name, Deck deck, String playerType) { - Player player = PlayerFactory.getInstance().createPlayer(playerType, name, deck, game.getRangeOfInfluence()); + Player player = PlayerFactory.getInstance().createPlayer(playerType, name, deck, options.getRange()); logger.info("Player created " + player.getId()); return player; } @@ -151,26 +155,43 @@ public class TableController { table.leaveTable(sessionPlayerMap.get(sessionId)); } - public synchronized void startGame(UUID sessionId) { + public synchronized void startMatch(UUID sessionId) { if (sessionId.equals(this.sessionId) && table.getState() == TableState.STARTING) { try { - table.initGame(game); + match.startMatch(); + startGame(); } catch (GameException ex) { logger.log(Level.SEVERE, null, ex); } - GameManager.getInstance().createGameSession(game, sessionPlayerMap, table.getId()); - SessionManager sessionManager = SessionManager.getInstance(); - for (Entry entry: sessionPlayerMap.entrySet()) { - sessionManager.getSession(entry.getKey()).gameStarted(game.getId(), entry.getValue()); - } + } + } + + private void startGame() throws GameException { + match.startGame(); + GameManager.getInstance().createGameSession(match.getGame(), sessionPlayerMap, table.getId()); + SessionManager sessionManager = SessionManager.getInstance(); + for (Entry entry: sessionPlayerMap.entrySet()) { + sessionManager.getSession(entry.getKey()).gameStarted(match.getGame().getId(), entry.getValue()); } } public void endGame() { + match.endGame(); table.endGame(); saveGame(); - GameManager.getInstance().removeGame(game.getId()); - game = null; + GameManager.getInstance().removeGame(match.getGame().getId()); + try { + if (!match.isMatchOver()) { + startGame(); + } + } catch (GameException ex) { + logger.log(Level.SEVERE, null, ex); + } + endMatch(); + } + + public void endMatch() { + match = null; } public void swapSeats(int seatNum1, int seatNum2) { @@ -188,17 +209,17 @@ public class TableController { private void saveGame() { try { - OutputStream file = new FileOutputStream("saved/" + game.getId().toString() + ".game"); + OutputStream file = new FileOutputStream("saved/" + match.getGame().getId().toString() + ".game"); OutputStream buffer = new BufferedOutputStream(file); ObjectOutput output = new ObjectOutputStream(new GZIPOutputStream(buffer)); try { - output.writeObject(game); - output.writeObject(game.getGameStates()); + output.writeObject(match.getGame()); + output.writeObject(match.getGame().getGameStates()); } finally { output.close(); } - logger.log(Level.INFO, "Saved game:" + game.getId()); + logger.log(Level.INFO, "Saved game:" + match.getGame().getId()); } catch(IOException ex) { logger.log(Level.SEVERE, "Cannot save game.", ex); @@ -207,7 +228,7 @@ public class TableController { private Game loadGame() { try{ - InputStream file = new FileInputStream("saved/" + gameId.toString() + ".game"); + InputStream file = new FileInputStream("saved/" + match.getGame().toString() + ".game"); InputStream buffer = new BufferedInputStream(file); ObjectInput input = new CopierObjectInputStream(Main.classLoader, new GZIPInputStream(buffer)); try { @@ -224,7 +245,7 @@ public class TableController { logger.log(Level.SEVERE, "Cannot load game. Class not found.", ex); } catch(IOException ex) { - logger.log(Level.SEVERE, "Cannot load game:" + game.getId(), ex); + logger.log(Level.SEVERE, "Cannot load game:" + match.getGame().getId(), ex); } return null; } diff --git a/Mage.Server/src/main/java/mage/server/game/TableManager.java b/Mage.Server/src/main/java/mage/server/game/TableManager.java index 9369d13574c..d0ffc29cc41 100644 --- a/Mage.Server/src/main/java/mage/server/game/TableManager.java +++ b/Mage.Server/src/main/java/mage/server/game/TableManager.java @@ -38,6 +38,8 @@ import mage.Constants.MultiplayerAttackOption; import mage.Constants.RangeOfInfluence; import mage.cards.decks.DeckCardLists; import mage.game.GameException; +import mage.game.match.MatchType; +import mage.game.match.MatchOptions; import mage.util.Logging; /** @@ -56,8 +58,8 @@ public class TableManager { return INSTANCE; } - public Table createTable(UUID sessionId, String gameType, String deckType, List playerTypes, MultiplayerAttackOption attackOption, RangeOfInfluence range) { - TableController tableController = new TableController(sessionId, gameType, deckType, playerTypes, attackOption, range); + public Table createTable(UUID sessionId, MatchOptions options) { + TableController tableController = new TableController(sessionId, options); controllers.put(tableController.getTable().getId(), tableController); tables.put(tableController.getTable().getId(), tableController.getTable()); return tableController.getTable(); @@ -100,8 +102,8 @@ public class TableManager { return controllers.get(tableId).getChatId(); } - public void startGame(UUID sessionId, UUID roomId, UUID tableId) { - controllers.get(tableId).startGame(sessionId); + public void startMatch(UUID sessionId, UUID roomId, UUID tableId) { + controllers.get(tableId).startMatch(sessionId); } public boolean watchTable(UUID sessionId, UUID tableId) { diff --git a/Mage.Tests/src/test/java/org/mage/test/base/MageBase.java b/Mage.Tests/src/test/java/org/mage/test/base/MageBase.java index b174d9bbf19..6bda8ab0fc9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/base/MageBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/base/MageBase.java @@ -21,6 +21,9 @@ import java.util.List; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; +import mage.Constants.MultiplayerAttackOption; +import mage.Constants.RangeOfInfluence; +import mage.game.match.MatchOptions; /** * Base for starting Mage server. @@ -64,14 +67,18 @@ public class MageBase { connect("player", "localhost", 17171); UUID roomId = server.getMainRoomId(); - List playerTypes = new ArrayList(); - playerTypes.add("Human"); - playerTypes.add("Computer - default"); - TableView table = server.createTable(sessionId, roomId, "Two Player Duel", "Limited", playerTypes, null, null); + MatchOptions options = new MatchOptions("1", "Two Player Duel"); + options.getPlayerTypes().add("Human"); + options.getPlayerTypes().add("Computer - default"); + options.setDeckType("Limited"); + options.setAttackOption(MultiplayerAttackOption.LEFT); + options.setRange(RangeOfInfluence.ALL); + options.setWinsNeeded(1); + TableView table = server.createTable(sessionId, roomId, options); System.out.println("Cards in the deck: " + Sets.loadDeck("UW Control.dck").getCards().size()); server.joinTable(sessionId, roomId, table.getTableId(), "Human", Sets.loadDeck("UW Control.dck")); server.joinTable(sessionId, roomId, table.getTableId(), "Computer", Sets.loadDeck("UW Control.dck")); - server.startGame(sessionId, roomId, table.getTableId()); + server.startMatch(sessionId, roomId, table.getTableId()); synchronized (syncStart) { int waitTime = 7000; diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 952c43e4cca..0bf3bc24f8f 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -28,6 +28,7 @@ package mage.game; +import mage.game.match.MatchType; import mage.cards.Card; import mage.game.stack.SpellStack; import mage.MageObject; @@ -64,7 +65,7 @@ import mage.players.Players; public interface Game extends MageItem, Serializable { - public GameType getGameType(); + public MatchType getGameType(); public int getNumPlayers(); public int getLife(); public RangeOfInfluence getRangeOfInfluence(); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 280324324a2..40b4ad16f05 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -28,6 +28,7 @@ package mage.game; +import mage.game.match.MatchType; import java.io.IOException; import mage.game.stack.SpellStack; import java.io.Serializable; @@ -315,7 +316,7 @@ public abstract class GameImpl> implements Game, Serializa player = players.getNext(this); } - winnerId = findWinner(); + winnerId = findWinnersAndLosers(); saveState(); } @@ -380,13 +381,21 @@ public abstract class GameImpl> implements Game, Serializa } } - protected UUID findWinner() { + protected UUID findWinnersAndLosers() { + UUID winner = null; for (Player player: state.getPlayers().values()) { if (player.hasWon() || (!player.hasLost() && !player.hasLeft())) { - return player.getId(); + player.won(this); + winner = player.getId(); + break; } } - return null; + for (Player player: state.getPlayers().values()) { + if (winner != null && !player.getId().equals(winner)) { + player.lost(this); + } + } + return winner; } protected void endOfTurn() { diff --git a/Mage/src/mage/game/Match.java b/Mage/src/mage/game/match/Match.java similarity index 87% rename from Mage/src/mage/game/Match.java rename to Mage/src/mage/game/match/Match.java index ad58ddd6508..0c938da2236 100644 --- a/Mage/src/mage/game/Match.java +++ b/Mage/src/mage/game/match/Match.java @@ -26,10 +26,13 @@ * or implied, of BetaSteward_at_googlemail.com. */ -package mage.game; +package mage.game.match; import java.util.List; +import java.util.UUID; import mage.cards.decks.Deck; +import mage.game.Game; +import mage.game.GameException; import mage.players.Player; /** @@ -38,11 +41,13 @@ import mage.players.Player; */ public interface Match { + public UUID getId(); public boolean isMatchOver(); public List getPlayers(); public void addPlayer(Player player, Deck deck); - public int getMaxPlayers(); - public int getMinPlayers(); - public void startMatch(); + public void startMatch() throws GameException; + public void startGame() throws GameException; + public void endGame(); + public Game getGame(); } diff --git a/Mage/src/mage/game/MatchImpl.java b/Mage/src/mage/game/match/MatchImpl.java similarity index 66% rename from Mage/src/mage/game/MatchImpl.java rename to Mage/src/mage/game/match/MatchImpl.java index 96e6243d11d..0dfa4e1d6de 100644 --- a/Mage/src/mage/game/MatchImpl.java +++ b/Mage/src/mage/game/match/MatchImpl.java @@ -26,27 +26,29 @@ * or implied, of BetaSteward_at_googlemail.com. */ -package mage.game; +package mage.game.match; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import mage.cards.decks.Deck; +import mage.game.Game; +import mage.game.GameException; import mage.players.Player; /** * * @author BetaSteward_at_googlemail.com */ -public class MatchImpl implements Match { +public abstract class MatchImpl implements Match { + protected UUID id = UUID.randomUUID(); protected List players = new ArrayList(); - protected List games = new ArrayList(); - protected int winsNeeded; - protected int maxPlayers; - protected int minPlayers; + protected List games = new ArrayList(); + protected MatchOptions options; - public MatchImpl(int winsNeeded) { - this.winsNeeded = winsNeeded; + public MatchImpl(MatchOptions options) { + this.options = options; } @Override @@ -61,14 +63,19 @@ public class MatchImpl implements Match { } @Override - public void startMatch() { + public void startMatch() throws GameException { } + @Override + public UUID getId() { + return id; + } + @Override public boolean isMatchOver() { for (MatchPlayer player: players) { - if (player.getWins() >= winsNeeded) { + if (player.getWins() >= options.getWinsNeeded()) { return true; } } @@ -76,13 +83,29 @@ public class MatchImpl implements Match { } @Override - public int getMaxPlayers() { - return this.maxPlayers; + public T getGame() { + return games.get(games.size() -1); + } + + protected void initGame(Game game) throws GameException { + for (MatchPlayer matchPlayer: this.players) { + game.addPlayer(matchPlayer.getPlayer()); + game.loadCards(matchPlayer.getDeck().getCards(), matchPlayer.getPlayer().getId()); + game.loadCards(matchPlayer.getDeck().getSideboard(), matchPlayer.getPlayer().getId()); + } } @Override - public int getMinPlayers() { - return this.minPlayers; + public void endGame() { + Game game = getGame(); + for (MatchPlayer player: this.players) { + Player p = game.getPlayer(player.getPlayer().getId()); + if (p != null) { + if (p.hasWon()) + player.addWin(); + if (p.hasLost()) + player.addLose(); + } + } } - } diff --git a/Mage/src/mage/game/match/MatchOptions.java b/Mage/src/mage/game/match/MatchOptions.java new file mode 100644 index 00000000000..5301c55e5bc --- /dev/null +++ b/Mage/src/mage/game/match/MatchOptions.java @@ -0,0 +1,104 @@ +/* + * 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.game.match; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import mage.Constants.MultiplayerAttackOption; +import mage.Constants.RangeOfInfluence; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class MatchOptions implements Serializable { + + protected String name; + protected MultiplayerAttackOption attackOption; + protected RangeOfInfluence range; + protected int winsNeeded; + protected String gameType; + protected String deckType; + protected List playerTypes = new ArrayList(); + + public MatchOptions(String name, String gameType) { + this.name = name; + this.gameType = gameType; + } + + public String getName() { + return name; + } + + public MultiplayerAttackOption getAttackOption() { + return attackOption; + } + + public void setAttackOption(MultiplayerAttackOption attackOption) { + this.attackOption = attackOption; + } + + public RangeOfInfluence getRange() { + return range; + } + + public void setRange(RangeOfInfluence range) { + this.range = range; + } + + public int getWinsNeeded() { + return winsNeeded; + } + + public void setWinsNeeded(int winsNeeded) { + this.winsNeeded = winsNeeded; + } + + public String getGameType() { + return gameType; + } + + public void setGameType(String gameType) { + this.gameType = gameType; + } + + public String getDeckType() { + return deckType; + } + + public void setDeckType(String deckType) { + this.deckType = deckType; + } + + public List getPlayerTypes() { + return playerTypes; + } + +} diff --git a/Mage/src/mage/game/MatchPlayer.java b/Mage/src/mage/game/match/MatchPlayer.java similarity index 98% rename from Mage/src/mage/game/MatchPlayer.java rename to Mage/src/mage/game/match/MatchPlayer.java index 4402b85c500..cf013ded155 100644 --- a/Mage/src/mage/game/MatchPlayer.java +++ b/Mage/src/mage/game/match/MatchPlayer.java @@ -26,7 +26,7 @@ * or implied, of BetaSteward_at_googlemail.com. */ -package mage.game; +package mage.game.match; import mage.cards.decks.Deck; import mage.players.Player; diff --git a/Mage/src/mage/game/GameType.java b/Mage/src/mage/game/match/MatchType.java similarity index 80% rename from Mage/src/mage/game/GameType.java rename to Mage/src/mage/game/match/MatchType.java index 4670e3da2e3..d3c58fece03 100644 --- a/Mage/src/mage/game/GameType.java +++ b/Mage/src/mage/game/match/MatchType.java @@ -26,15 +26,17 @@ * or implied, of BetaSteward_at_googlemail.com. */ -package mage.game; +package mage.game.match; import java.io.Serializable; +import mage.Constants.MultiplayerAttackOption; +import mage.Constants.RangeOfInfluence; /** * * @author BetaSteward_at_googlemail.com */ -public abstract class GameType implements Serializable { +public abstract class MatchType> implements Serializable { protected String name; protected int minPlayers; @@ -44,6 +46,20 @@ public abstract class GameType implements Serializable { protected boolean useRange; protected boolean useAttackOption; + protected MatchType() {} + + protected MatchType(final MatchType matchType) { + this.name = matchType.name; + this.maxPlayers = matchType.maxPlayers; + this.minPlayers = matchType.minPlayers; + this.numTeams = matchType.numTeams; + this.playersPerTeam = matchType.playersPerTeam; + this.useRange = matchType.useRange; + this.useAttackOption = matchType.useAttackOption; + } + + public abstract T copy(); + @Override public String toString() { return name; From 3123207b0d5ea1560ae9d93dcf986130a5952303 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Sun, 26 Dec 2010 18:40:28 +0300 Subject: [PATCH 02/23] New tooltips. --- Mage.Client/plugins/mage-card-plugin.jar | Bin 306004 -> 306121 bytes .../src/main/java/mage/client/MageFrame.java | 43 +-- .../client/components/MageComponents.java | 3 +- .../mage/client/components/MageJDesktop.java | 34 +++ .../mage/client/components/MageRoundPane.java | 97 ++++++ .../mage/client/game/BattlefieldPanel.java | 32 +- .../java/mage/client/plugins/MagePlugins.java | 4 +- .../plugins/adapters/MageActionCallback.java | 283 +++++++++--------- .../mage/client/plugins/impl/Plugins.java | 8 +- .../mage/client/util/gui/GuiDisplayUtil.java | 8 +- .../mage/interfaces/plugin/CardPlugin.java | 4 +- .../java/org/mage/card/arcane/Animation.java | 14 +- .../java/org/mage/card/arcane/CardPanel.java | 7 + .../org/mage/plugins/card/CardPluginImpl.java | 8 +- .../plugins/card/info/CardInfoPaneImpl.java | 162 +++++----- 15 files changed, 432 insertions(+), 275 deletions(-) create mode 100644 Mage.Client/src/main/java/mage/client/components/MageJDesktop.java create mode 100644 Mage.Client/src/main/java/mage/client/components/MageRoundPane.java diff --git a/Mage.Client/plugins/mage-card-plugin.jar b/Mage.Client/plugins/mage-card-plugin.jar index cf8452b5b0dda8e45578b1c9129d02781ad217fd..8de1c45eb491f8935e321226e4cbc9f34b8a33b5 100644 GIT binary patch delta 33173 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//GEN-BEGIN:initComponents private void initComponents() { - desktopPane = new javax.swing.JDesktopPane(); + desktopPane = new MageJDesktop(); tablesPane = new mage.client.table.TablesPane(); gamePane = new mage.client.game.GamePane(); deckEditorPane = new mage.client.deckeditor.DeckEditorPane(); @@ -595,7 +598,7 @@ public class MageFrame extends javax.swing.JFrame { private javax.swing.JButton btnExit; private javax.swing.JButton btnGames; private mage.client.deckeditor.DeckEditorPane deckEditorPane; - private static javax.swing.JDesktopPane desktopPane; + private static MageJDesktop desktopPane; private mage.client.game.GamePane gamePane; private javax.swing.JToolBar.Separator jSeparator1; private javax.swing.JToolBar.Separator jSeparator2; diff --git a/Mage.Client/src/main/java/mage/client/components/MageComponents.java b/Mage.Client/src/main/java/mage/client/components/MageComponents.java index 3259cba94b6..1a8bce85d82 100644 --- a/Mage.Client/src/main/java/mage/client/components/MageComponents.java +++ b/Mage.Client/src/main/java/mage/client/components/MageComponents.java @@ -6,7 +6,8 @@ public enum MageComponents { NEW_TABLE_OK_BUTTON("btnOK"), TABLE_WAITING_START_BUTTON("btnStart"), DESKTOP_PANE("desktopPane"), - CARD_INFO_PANE("cardInfoPane"); + CARD_INFO_PANE("cardInfoPane"), + POPUP_CONTAINER("popupContainer"); private String name; MageComponents(String name) { diff --git a/Mage.Client/src/main/java/mage/client/components/MageJDesktop.java b/Mage.Client/src/main/java/mage/client/components/MageJDesktop.java new file mode 100644 index 00000000000..a73976844ec --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/components/MageJDesktop.java @@ -0,0 +1,34 @@ +package mage.client.components; + +import com.sun.java.swing.Painter; + +import javax.swing.*; +import java.awt.*; + +/** + * Overrides JDesktopPane settings for Nimbus LAF. + * + * @author nantuko + */ +public class MageJDesktop extends JDesktopPane { + + @Override + public void updateUI() { + if ("Nimbus".equals(UIManager.getLookAndFeel().getName())) { + UIDefaults map = new UIDefaults(); + Painter painter = new Painter() { + + Color color = null; + + @Override + public void paint(Graphics2D g, Object c, int w, int h) { + g.setColor(color == null ? UIManager.getDefaults().getColor("desktop") : color); + g.fillRect(0,0,w,h); + } + }; + map.put("DesktopPane[Enabled].backgroundPainter", painter); + putClientProperty("Nimbus.Overrides", map); + } + super.updateUI(); + } +} diff --git a/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java b/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java new file mode 100644 index 00000000000..1460445506d --- /dev/null +++ b/Mage.Client/src/main/java/mage/client/components/MageRoundPane.java @@ -0,0 +1,97 @@ +package mage.client.components; + +import org.jdesktop.swingx.graphics.GraphicsUtilities; +import org.jdesktop.swingx.graphics.ShadowRenderer; + +import java.awt.*; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; + +import javax.swing.JPanel; + +/** + * Mage round pane with transparency. + * Used for tooltips. + * + * @author nantuko + */ +public class MageRoundPane extends JPanel { + + private static int X_OFFSET = 30; + private static int Y_OFFSET = 30; + private BufferedImage shadow = null; + private Color backgroundColor = new Color(255, 255, 255, 220); + private int alpha = 0; + + @Override + protected void paintComponent(Graphics g) { + int x = X_OFFSET; + int y = Y_OFFSET; + int w = getWidth() - 2 * X_OFFSET; + int h = getHeight() - 2 * Y_OFFSET; + int arc = 10; + + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + if (shadow != null) { + int xOffset = (shadow.getWidth() - w) / 2; + int yOffset = (shadow.getHeight() - h) / 2; + g2.drawImage(shadow, x - xOffset, y - yOffset, null); + } + + ////////////////////////////////////////////////////////////////// + // fill content + + /** + * Add white translucent substrate + */ + /*if (alpha != 0) { + g2.setColor(new Color(255, 255, 255, alpha)); + g2.fillRoundRect(x, y, w, h, arc, arc); + }*/ + + g2.setColor(backgroundColor); + g2.fillRoundRect(x, y, w, h, arc, arc); + ////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////// + // draw border + g2.setStroke(new BasicStroke(1.5f)); + g2.setColor(Color.BLACK); + g2.drawRoundRect(x, y, w, h, arc, arc); + // //////////////////////////////////////////////////////////////// + + g2.dispose(); + } + + @Override + public void setBounds(int x, int y, int width, int height) { + super.setBounds(x, y, width, height); + + int w = getWidth() - 2 * X_OFFSET; + int h = getHeight() - 2 * Y_OFFSET; + int arc = 10; + int shadowSize = 50; + + shadow = GraphicsUtilities.createCompatibleTranslucentImage(w, h); + Graphics2D g2 = shadow.createGraphics(); + g2.setColor(Color.WHITE); + g2.fillRoundRect(0, 0, w, h, arc, arc); + g2.dispose(); + + ShadowRenderer renderer = new ShadowRenderer(shadowSize, 0.5f, + Color.GRAY); + shadow = renderer.createShadow(shadow); + } + + public void showDialog(boolean bShow) { + setVisible(bShow); + } + + /** + * Default UID. + */ + private static final long serialVersionUID = 1L; +} diff --git a/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java b/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java index 70551b3b346..349affb8d6c 100644 --- a/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/BattlefieldPanel.java @@ -96,14 +96,14 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { } public void update(Map battlefield) { - boolean changed = false; - + boolean changed = false; + + List permanentsToAdd = new ArrayList(); for (PermanentView permanent: battlefield.values()) { if (!permanents.containsKey(permanent.getId())) { - addPermanent(permanent); + permanentsToAdd.add(permanent); changed = true; - } - else { + } else { MagePermanent p = permanents.get(permanent.getId()); if (!changed) { int s1 = permanent.getAttachments() == null ? 0 : permanent.getAttachments().size(); @@ -115,10 +115,15 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { permanents.get(permanent.getId()).update(permanent); } } + int count = permanentsToAdd.size(); + for (PermanentView permanent : permanentsToAdd) { + addPermanent(permanent, count); + } + for (Iterator> i = permanents.entrySet().iterator(); i.hasNext();) { Entry entry = i.next(); if (!battlefield.containsKey(entry.getKey())) { - removePermanent(entry.getKey()); + removePermanent(entry.getKey(), 1); i.remove(); changed = true; } @@ -150,12 +155,12 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { repaint(); } - private void addPermanent(PermanentView permanent) { + private void addPermanent(PermanentView permanent, final int count) { final MagePermanent perm = Plugins.getInstance().getMagePermanent(permanent, bigCard, Config.dimensions, gameId); if (!Plugins.getInstance().isCardPluginLoaded()) { perm.setBounds(findEmptySpace(new Dimension(Config.dimensions.frameWidth, Config.dimensions.frameHeight))); } else { - perm.setAlpha(0); + //perm.setAlpha(0); } permanents.put(permanent.getId(), perm); @@ -164,15 +169,16 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { moveToFront(perm); perm.update(permanent); } else { - Thread t = new Thread(new Runnable() { + Plugins.getInstance().onAddCard(perm, 1); + /*Thread t = new Thread(new Runnable() { @Override public void run() { - Plugins.getInstance().onAddCard(perm); + Plugins.getInstance().onAddCard(perm, count); } }); synchronized (this) { threads.add(t); - } + }*/ } } @@ -208,7 +214,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { } - private void removePermanent(UUID permanentId) { + private void removePermanent(UUID permanentId, final int count) { for (Component c: this.getComponents()) { final Component comp = c; if (comp instanceof Permanent) { @@ -220,7 +226,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane { Thread t = new Thread(new Runnable() { @Override public void run() { - Plugins.getInstance().onRemoveCard((MagePermanent)comp); + Plugins.getInstance().onRemoveCard((MagePermanent)comp, count); BattlefieldPanel.this.remove(comp); } }); diff --git a/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java b/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java index 61807e13ed9..e2fb11d6719 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java +++ b/Mage.Client/src/main/java/mage/client/plugins/MagePlugins.java @@ -32,7 +32,7 @@ public interface MagePlugins { int getGamesPlayed(); void addGamesPlayed(); Image getManaSymbolImage(String symbol); - void onAddCard(MagePermanent card); - void onRemoveCard(MagePermanent card); + void onAddCard(MagePermanent card, int count); + void onRemoveCard(MagePermanent card, int count); JComponent getCardInfoPane(); } diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java index 089dae89e3f..d1cabb2ffdb 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java @@ -4,6 +4,7 @@ import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; +import java.io.InterruptedIOException; import java.util.List; import java.util.UUID; @@ -16,6 +17,7 @@ import mage.cards.action.TransferData; import mage.client.MageFrame; import mage.client.cards.BigCard; import mage.client.components.MageComponents; +import mage.client.components.MageRoundPane; import mage.client.game.PlayAreaPanel; import mage.client.plugins.impl.Plugins; import mage.client.remote.Session; @@ -31,95 +33,95 @@ import org.jdesktop.swingx.JXPanel; public class MageActionCallback implements ActionCallback { - private Popup popup; + private Popup popup; private JPopupMenu jPopupMenu; - private BigCard bigCard; - protected static DefaultActionCallback defaultCallback = DefaultActionCallback.getInstance(); - protected static Session session = MageFrame.getSession(); - private CardView popupCard; - private Thread t; - private int state = 0; + private BigCard bigCard; + protected static DefaultActionCallback defaultCallback = DefaultActionCallback.getInstance(); + protected static Session session = MageFrame.getSession(); + private CardView popupCard; + private Thread t; + private int state = 0; private JComponent cardInfoPane; - - public MageActionCallback() { - } - - public void setCardPreviewComponent(BigCard bigCard) { - this.bigCard = bigCard; - } - - public void refreshSession() { - if (session == null) { - session = MageFrame.getSession(); - } + + public MageActionCallback() { + } + + public void setCardPreviewComponent(BigCard bigCard) { + this.bigCard = bigCard; + } + + public void refreshSession() { + if (session == null) { + session = MageFrame.getSession(); + } if (cardInfoPane == null) { cardInfoPane = Plugins.getInstance().getCardInfoPane(); } - } - - @Override - public void mouseClicked(MouseEvent e, TransferData data) { - } - - @Override - public void mousePressed(MouseEvent e, TransferData data) { - data.component.requestFocusInWindow(); - defaultCallback.mouseClicked(e, data.gameId, session, data.card); - } + } - @Override - public void mouseEntered(MouseEvent e, final TransferData data) { - hidePopup(); + @Override + public void mouseClicked(MouseEvent e, TransferData data) { + } + + @Override + public void mousePressed(MouseEvent e, TransferData data) { + data.component.requestFocusInWindow(); + defaultCallback.mouseClicked(e, data.gameId, session, data.card); + } + + @Override + public void mouseEntered(MouseEvent e, final TransferData data) { + hidePopup(); this.popupCard = data.card; Component parentComponent = SwingUtilities.getRoot(data.component); Point parentPoint = parentComponent.getLocationOnScreen(); - // Draw Arrows for targets - List targets = data.card.getTargets(); - if (targets != null) { - Point me = new Point(data.locationOnScreen); - me.translate(-parentPoint.x, -parentPoint.y); - for (UUID uuid : targets) { - //System.out.println("Getting play area panel for uuid: " + uuid); - - PlayAreaPanel p = session.getGame().getPlayers().get(uuid); - if (p != null) { - Point target = p.getLocationOnScreen(); - target.translate(-parentPoint.x, -parentPoint.y); - ArrowBuilder.addArrow((int)me.getX() + 35, (int)me.getY(), (int)target.getX() + 40, (int)target.getY() - 40, Color.red); - } else { - for (PlayAreaPanel pa : session.getGame().getPlayers().values()) { - MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); - if (permanent != null) { - Point target = permanent.getLocationOnScreen(); - target.translate(-parentPoint.x, -parentPoint.y); - ArrowBuilder.addArrow((int)me.getX() + 35, (int)me.getY(), (int)target.getX() + 40, (int)target.getY() + 10, Color.red); - } - } - } - } - } - - // Draw Arrows for source - if (data.card.isAbility()) { - Point me = new Point(data.locationOnScreen); - me.translate(-parentPoint.x, -parentPoint.y); - UUID uuid = data.card.getParentId(); - for (PlayAreaPanel pa : session.getGame().getPlayers().values()) { - MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); - if (permanent != null) { - Point source = permanent.getLocationOnScreen(); - source.translate(-parentPoint.x, -parentPoint.y); - ArrowBuilder.addArrow((int)source.getX() + 40, (int)source.getY() + 10, (int)me.getX() + 35, (int)me.getY() + 20, Color.blue); - } - } - } - - showPopup(data, parentComponent, parentPoint); - } + // Draw Arrows for targets + List targets = data.card.getTargets(); + if (targets != null) { + Point me = new Point(data.locationOnScreen); + me.translate(-parentPoint.x, -parentPoint.y); + for (UUID uuid : targets) { + //System.out.println("Getting play area panel for uuid: " + uuid); - private void showPopup(final TransferData data, final Component parentComponent, final Point parentPoint) { + PlayAreaPanel p = session.getGame().getPlayers().get(uuid); + if (p != null) { + Point target = p.getLocationOnScreen(); + target.translate(-parentPoint.x, -parentPoint.y); + ArrowBuilder.addArrow((int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() - 40, Color.red); + } else { + for (PlayAreaPanel pa : session.getGame().getPlayers().values()) { + MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); + if (permanent != null) { + Point target = permanent.getLocationOnScreen(); + target.translate(-parentPoint.x, -parentPoint.y); + ArrowBuilder.addArrow((int) me.getX() + 35, (int) me.getY(), (int) target.getX() + 40, (int) target.getY() + 10, Color.red); + } + } + } + } + } + + // Draw Arrows for source + if (data.card.isAbility()) { + Point me = new Point(data.locationOnScreen); + me.translate(-parentPoint.x, -parentPoint.y); + UUID uuid = data.card.getParentId(); + for (PlayAreaPanel pa : session.getGame().getPlayers().values()) { + MagePermanent permanent = pa.getBattlefieldPanel().getPermanents().get(uuid); + if (permanent != null) { + Point source = permanent.getLocationOnScreen(); + source.translate(-parentPoint.x, -parentPoint.y); + ArrowBuilder.addArrow((int) source.getX() + 40, (int) source.getY() + 10, (int) me.getX() + 35, (int) me.getY() + 20, Color.blue); + } + } + } + + showPopup(data, parentComponent, parentPoint); + } + + private void showPopup(final TransferData data, final Component parentComponent, final Point parentPoint) { if (cardInfoPane == null) { PopupFactory factory = PopupFactory.getSharedInstance(); popup = factory.getPopup(data.component, data.popupText, (int) data.locationOnScreen.getX() + data.popupOffsetX, (int) data.locationOnScreen.getY() + data.popupOffsetY + 40); @@ -133,72 +135,73 @@ public class MageActionCallback implements ActionCallback { ThreadUtils.threadPool2.submit(new Runnable() { @Override public void run() { - ThreadUtils.threadPool2.submit(new Runnable() { - @Override - public void run() { - ThreadUtils.sleep(700); + ThreadUtils.sleep(700); - if (!popupCard.equals(data.card)) { - return; - } + if (!popupCard.equals(data.card)) { + return; + } - /*PopupFactory factory = PopupFactory.getSharedInstance(); - ((CardInfoPane)cardInfoPane).setCard(data.card); - cardInfoPane.setSize(161, 221); - cardInfoPane.setPreferredSize(new Dimension(161, 221)); - popup = factory.getPopup(data.component, cardInfoPane, (int) data.locationOnScreen.getX() + data.popupOffsetX, (int) data.locationOnScreen.getY() + data.popupOffsetY + 40); - popup.show(); - */ - - try { - Component popup2 = session.getUI().getComponent(MageComponents.CARD_INFO_PANE); - ((CardInfoPane)popup2).setCard(data.card); - Point location = new Point((int) data.locationOnScreen.getX() + data.popupOffsetX, (int) data.locationOnScreen.getY() + data.popupOffsetY + 40); - location = GuiDisplayUtil.keepComponentInsideParent(location, parentPoint, popup2, parentComponent); - location.translate(-parentPoint.x, -parentPoint.y); - popup2.setLocation(location); - ThreadUtils.sleep(200); - popup2.setVisible(true); - } catch (Exception e) { - e.printStackTrace(); + try { + final Component popupContainer = session.getUI().getComponent(MageComponents.POPUP_CONTAINER); + Component popup2 = session.getUI().getComponent(MageComponents.CARD_INFO_PANE); + ((CardInfoPane) popup2).setCard(data.card); + Point location = new Point((int) data.locationOnScreen.getX() + data.popupOffsetX - 40, (int) data.locationOnScreen.getY() + data.popupOffsetY - 40); + location = GuiDisplayUtil.keepComponentInsideParent(location, parentPoint, popup2, parentComponent); + location.translate(-parentPoint.x, -parentPoint.y); + popupContainer.setLocation(location); + ThreadUtils.sleep(200); + final Component c = session.getUI().getComponent(MageComponents.DESKTOP_PANE); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + popupContainer.setVisible(true); + c.repaint(); } } - }); + ); + } catch (Exception e) { + e.printStackTrace(); + } } }); } } - - @Override - public void mouseMoved(MouseEvent e, TransferData data) { - if (!Plugins.getInstance().isCardPluginLoaded()) {return;} - if (bigCard == null) {return;} - MageCard card = (MageCard) data.component; - if (card.getOriginal().getId() != bigCard.getCardId()) { - synchronized (MageActionCallback.class) { - if (card.getOriginal().getId() != bigCard.getCardId()) { - Image image = card.getImage(); - if (image != null && image instanceof BufferedImage) { - image = ImageHelper.getResizedImage((BufferedImage) image, bigCard.getWidth(), bigCard.getHeight()); - bigCard.setCard(card.getOriginal().getId(), image, card.getOriginal().getRules()); - bigCard.showTextComponent(); - if (card.getOriginal().isAbility()) { - bigCard.showTextComponent(); - } else { - bigCard.hideTextComponent(); - }; - } else { - JXPanel panel = GuiDisplayUtil.getDescription(card.getOriginal(), bigCard.getWidth(), bigCard.getHeight()); - panel.setVisible(true); - bigCard.hideTextComponent(); - bigCard.addJXPanel(card.getOriginal().getId(), panel); - } - } - } - } - } + @Override + public void mouseMoved(MouseEvent e, TransferData data) { + if (!Plugins.getInstance().isCardPluginLoaded()) { + return; + } + if (bigCard == null) { + return; + } + + MageCard card = (MageCard) data.component; + if (card.getOriginal().getId() != bigCard.getCardId()) { + synchronized (MageActionCallback.class) { + if (card.getOriginal().getId() != bigCard.getCardId()) { + Image image = card.getImage(); + if (image != null && image instanceof BufferedImage) { + image = ImageHelper.getResizedImage((BufferedImage) image, bigCard.getWidth(), bigCard.getHeight()); + bigCard.setCard(card.getOriginal().getId(), image, card.getOriginal().getRules()); + bigCard.showTextComponent(); + if (card.getOriginal().isAbility()) { + bigCard.showTextComponent(); + } else { + bigCard.hideTextComponent(); + } + ; + } else { + JXPanel panel = GuiDisplayUtil.getDescription(card.getOriginal(), bigCard.getWidth(), bigCard.getHeight()); + panel.setVisible(true); + bigCard.hideTextComponent(); + bigCard.addJXPanel(card.getOriginal().getId(), panel); + } + } + } + } + } private void hidePopup() { this.popupCard = null; @@ -209,17 +212,17 @@ public class MageActionCallback implements ActionCallback { jPopupMenu.setVisible(false); } try { - Component popup2 = session.getUI().getComponent(MageComponents.CARD_INFO_PANE); - popup2.setVisible(false); + Component popupContainer = session.getUI().getComponent(MageComponents.POPUP_CONTAINER); + popupContainer.setVisible(false); } catch (Exception e2) { - e2.printStackTrace();; + e2.printStackTrace(); } } - @Override - public void mouseExited(MouseEvent e, final TransferData data) { + @Override + public void mouseExited(MouseEvent e, final TransferData data) { hidePopup(); - ArrowBuilder.removeAllArrows(); - } + ArrowBuilder.removeAllArrows(); + } } diff --git a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java index eef3fa4139e..8b999ea4a16 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java +++ b/Mage.Client/src/main/java/mage/client/plugins/impl/Plugins.java @@ -169,16 +169,16 @@ public class Plugins implements MagePlugins { } @Override - public void onAddCard(MagePermanent card) { + public void onAddCard(MagePermanent card, int count) { if (this.cardPlugin != null) { - this.cardPlugin.onAddCard(card); + this.cardPlugin.onAddCard(card, count); } } @Override - public void onRemoveCard(MagePermanent card) { + public void onRemoveCard(MagePermanent card, int count) { if (this.cardPlugin != null) { - this.cardPlugin.onRemoveCard(card); + this.cardPlugin.onRemoveCard(card, count); } } diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java index 9da4c86c1c2..2027dfdede2 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java @@ -11,10 +11,12 @@ import mage.utils.CardUtil; import mage.view.CardView; import org.jdesktop.swingx.JXPanel; +import sun.plugin.com.event.COMEventHandler; public class GuiDisplayUtil { private static final Font cardNameFont = new Font("Calibri", Font.BOLD, 15); - private static Insets DEFAULT_INSETS = new Insets(0,0, 68, 15); + private static Insets DEFAULT_INSETS = new Insets(0, 0, 70, 25); + private static Insets COMPONENT_INSETS = new Insets(0, 0, 40, 40); public static JXPanel getDescription(CardView card, int width, int height) { JXPanel descriptionPanel = new JXPanel(); @@ -127,12 +129,12 @@ public class GuiDisplayUtil { } public static Point keepComponentInsideParent(Point l, Point parentPoint, Component c, Component parent) { - int dx = parentPoint.x + parent.getWidth() - DEFAULT_INSETS.right; + int dx = parentPoint.x + parent.getWidth() - DEFAULT_INSETS.right - COMPONENT_INSETS.right; if (l.x + c.getWidth() > dx) { l.x = dx - c.getWidth(); } - int dy = parentPoint.y + parent.getHeight() - DEFAULT_INSETS.bottom; + int dy = parentPoint.y + parent.getHeight() - DEFAULT_INSETS.bottom - COMPONENT_INSETS.bottom; if (l.y + c.getHeight() > dy) { l.y = Math.max(10, dy - c.getHeight()); } diff --git a/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java b/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java index 4ba61be4d29..7d8bf3b659e 100644 --- a/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java +++ b/Mage.Common/src/mage/interfaces/plugin/CardPlugin.java @@ -31,7 +31,7 @@ public interface CardPlugin extends Plugin { void downloadImages(Set allCards); void downloadSymbols(); Image getManaSymbolImage(String symbol); - void onAddCard(MagePermanent card); - void onRemoveCard(MagePermanent card); + void onAddCard(MagePermanent card, int count); + void onRemoveCard(MagePermanent card, int count); JComponent getCardInfoPane(); } diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Animation.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Animation.java index 8f5ea355ab2..e700ed66e07 100644 --- a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Animation.java +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/Animation.java @@ -287,8 +287,11 @@ abstract public class Animation { } } - static public void showCard(final MagePermanent card) { - new Animation(600) { + static public void showCard(final MagePermanent card, int count) { + if (count == 0) { + count = 1; + } + new Animation(600 / count) { protected void start () { } @@ -304,8 +307,11 @@ abstract public class Animation { }; } - static public void hideCard(final MagePermanent card) { - new Animation(600) { + static public void hideCard(final MagePermanent card, int count) { + if (count == 0) { + count = 1; + } + new Animation(600 / count) { protected void start () { } diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java index 37a4811e4d3..184b4c292b1 100644 --- a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java @@ -416,6 +416,13 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti @Override public void setAlpha(float alpha) { this.alpha = alpha; + if (alpha == 0) { + this.ptText.setVisible(false); + this.titleText.setVisible(false); + } else if (alpha == 1.0f) { + this.ptText.setVisible(true); + this.titleText.setVisible(true); + } } public float getAlpha() { diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/CardPluginImpl.java index 7afd87da181..9fec2f9d2cb 100644 --- a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/CardPluginImpl.java +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/CardPluginImpl.java @@ -438,9 +438,9 @@ public class CardPluginImpl implements CardPlugin { } @Override - public void onAddCard(MagePermanent card) { + public void onAddCard(MagePermanent card, int count) { if (card != null) { - Animation.showCard((CardPanel) card); + Animation.showCard((CardPanel) card, count > 0 ? count : 1); try { while ((card).getAlpha() + 0.05f < 1) { Thread.sleep(30); @@ -452,9 +452,9 @@ public class CardPluginImpl implements CardPlugin { } @Override - public void onRemoveCard(MagePermanent card) { + public void onRemoveCard(MagePermanent card, int count) { if (card != null) { - Animation.hideCard((CardPanel) card); + Animation.hideCard((CardPanel) card, count > 0 ? count : 1); try { while ((card).getAlpha() - 0.05f > 0) { Thread.sleep(30); diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java index f542561d5e6..ac54bf140ad 100644 --- a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java @@ -24,98 +24,77 @@ public class CardInfoPaneImpl extends JEditorPane implements CardInfoPane { public CardInfoPaneImpl() { UI.setHTMLEditorKit(this); - setEditable(false); - setBackground(Color.white); - setSize(170, Integer.MAX_VALUE); + setEditable(false); + setBackground(Color.white); } - public void setCard (final CardView card) { + public void setCard(final CardView card) { if (card == null) return; - if (isCurrentCard(card)) return; - currentCard = card; + if (isCurrentCard(card)) return; + currentCard = card; ThreadUtils.threadPool.submit(new Runnable() { - public void run () { - if (!card.equals(currentCard)) return; + public void run() { + if (!card.equals(currentCard)) return; String manaCost = ""; for (String m : card.getManaCost()) { manaCost += m; } String castingCost = UI.getDisplayManaCost(manaCost); - castingCost = ManaSymbols.replaceSymbolsWithHTML(castingCost, false); + castingCost = ManaSymbols.replaceSymbolsWithHTML(castingCost, false); int symbolCount = 0; - int offset = 0; - while ((offset = castingCost.indexOf(" rulings = card.getRules(); boolean smallImages = true; - int fontSize = 11; + int fontSize = 11; - String fontFamily = "tahoma"; - /*if (prefs.fontFamily == CardFontFamily.arial) - fontFamily = "arial"; - else if (prefs.fontFamily == CardFontFamily.verdana) { - fontFamily = "verdana"; - }*/ + String fontFamily = "tahoma"; + /*if (prefs.fontFamily == CardFontFamily.arial) + fontFamily = "arial"; + else if (prefs.fontFamily == CardFontFamily.verdana) { + fontFamily = "verdana"; + }*/ - final StringBuffer buffer = new StringBuffer(512); - buffer.append(""); - buffer.append(""); - buffer.append("
    "); - buffer.append(card.getName()); - buffer.append(""); - buffer.append(castingCost); - buffer.append("
    "); - buffer.append("
    "); - buffer.append(getTypes(card)); + final StringBuffer buffer = new StringBuffer(512); + buffer.append(""); + buffer.append(""); + buffer.append("
    "); + buffer.append(card.getName()); + buffer.append(""); + buffer.append(castingCost); + buffer.append("
    "); + buffer.append("
    "); + buffer.append(getTypes(card)); buffer.append(""); switch (card.getRarity()) { - case RARE: - buffer.append(""); - break; - case UNCOMMON: - buffer.append(""); - break; - case COMMON: - buffer.append(""); - break; + case RARE: + buffer.append(""); + break; + case UNCOMMON: + buffer.append(""); + break; + case COMMON: + buffer.append(""); + break; case MYTHIC: - buffer.append(""); - break; - } - buffer.append(card.getExpansionSetCode().toUpperCase()); + buffer.append(""); + break; + } + buffer.append(card.getExpansionSetCode().toUpperCase()); buffer.append("
    "); - String legal = ""; - if (rulings.size() > 0) { - legal = legal.replaceAll("#([^#]+)#", "$1"); - legal = legal.replaceAll("\\s*//\\s*", "
    "); - legal = legal.replace("\r\n", "
    "); - legal += "
    "; - for (String ruling : rulings) { - legal += "

    "; - legal += ruling; - legal += "

    "; - } - } - - if (legal.length() > 0) { - buffer.append("
    "); - legal = legal.replaceAll("\\{this\\}", card.getName()); - legal = legal.replaceAll("\\{source\\}", card.getName()); - buffer.append(ManaSymbols.replaceSymbolsWithHTML(legal, smallImages)); - } - String pt = ""; if (CardUtil.isCreature(card)) { pt = card.getPower() + "/" + card.getToughness(); @@ -124,23 +103,42 @@ public class CardInfoPaneImpl extends JEditorPane implements CardInfoPane { } if (pt.length() > 0) { buffer.append("
    "); - buffer.append(""); - buffer.append(""); - buffer.append(pt); - buffer.append(""); - buffer.append("
    "); + buffer.append(""); + buffer.append(pt); + buffer.append(""); + buffer.append("
    "); + } + + String legal = ""; + if (rulings.size() > 0) { + legal = legal.replaceAll("#([^#]+)#", "$1"); + legal = legal.replaceAll("\\s*//\\s*", "
    "); + legal = legal.replace("\r\n", "
    "); + legal += "
    "; + for (String ruling : rulings) { + legal += "

    "; + legal += ruling; + legal += "

    "; + } + } + + if (legal.length() > 0) { + //buffer.append("
    "); + legal = legal.replaceAll("\\{this\\}", card.getName()); + legal = legal.replaceAll("\\{source\\}", card.getName()); + buffer.append(ManaSymbols.replaceSymbolsWithHTML(legal, smallImages)); } buffer.append("
    "); SwingUtilities.invokeLater(new Runnable() { - public void run () { - if (!card.equals(currentCard)) return; - setText(buffer.toString()); + public void run() { + if (!card.equals(currentCard)) return; + setText(buffer.toString()); //System.out.println(buffer.toString()); - setCaretPosition(0); - } - }); + setCaretPosition(0); + } + }); } }); } @@ -162,7 +160,7 @@ public class CardInfoPaneImpl extends JEditorPane implements CardInfoPane { return types.trim(); } - public boolean isCurrentCard (CardView card) { - return currentCard != null && card.equals(currentCard); - } + public boolean isCurrentCard(CardView card) { + return currentCard != null && card.equals(currentCard); + } } From 7bc6a97a449a620515437b91bd8216f1f5eef9b7 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Sun, 26 Dec 2010 22:38:22 +0300 Subject: [PATCH 03/23] [deck.generator] Added Set.findCard(name, random). Now decks contain random basic lands (against previous M11 only). --- .../client/deck/generator/DeckGenerator.java | 12 +++++---- Mage.Sets/src/mage/sets/Sets.java | 25 +++++++++++++++---- Mage/src/mage/cards/ExpansionSet.java | 13 ++++++++++ 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java index 4aaddd57fa6..fc01ce9129a 100644 --- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java +++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java @@ -46,6 +46,8 @@ public class DeckGenerator { private static final int MIN_SOURCE = 16; private static final int MAX_NON_BASIC_SOURCE = DECK_LANDS / 2; + private static final boolean GENERATE_RANDOM_BASIC_LAND = true; + private static Deck deck = new Deck(); private static String manaSource; @@ -283,19 +285,19 @@ public class DeckGenerator { private static Card getBestBasicLand(ColoredManaSymbol color) { manaSource = color.toString(); if (color.equals(ColoredManaSymbol.G)) { - return CardImpl.createCard(Sets.findCard("Forest")); + return CardImpl.createCard(Sets.findCard("Forest", GENERATE_RANDOM_BASIC_LAND)); } if (color.equals(ColoredManaSymbol.R)) { - return CardImpl.createCard(Sets.findCard("Mountain")); + return CardImpl.createCard(Sets.findCard("Mountain", GENERATE_RANDOM_BASIC_LAND)); } if (color.equals(ColoredManaSymbol.B)) { - return CardImpl.createCard(Sets.findCard("Swamp")); + return CardImpl.createCard(Sets.findCard("Swamp", GENERATE_RANDOM_BASIC_LAND)); } if (color.equals(ColoredManaSymbol.U)) { - return CardImpl.createCard(Sets.findCard("Island")); + return CardImpl.createCard(Sets.findCard("Island", GENERATE_RANDOM_BASIC_LAND)); } if (color.equals(ColoredManaSymbol.W)) { - return CardImpl.createCard(Sets.findCard("Plains")); + return CardImpl.createCard(Sets.findCard("Plains", GENERATE_RANDOM_BASIC_LAND)); } return null; diff --git a/Mage.Sets/src/mage/sets/Sets.java b/Mage.Sets/src/mage/sets/Sets.java index fd2bb98f0fc..f40e8fdb060 100644 --- a/Mage.Sets/src/mage/sets/Sets.java +++ b/Mage.Sets/src/mage/sets/Sets.java @@ -31,11 +31,7 @@ package mage.sets; import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; -import java.util.HashMap; -import java.util.Map; -import java.util.Scanner; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import mage.cards.Card; @@ -51,6 +47,7 @@ public class Sets extends HashMap { private static final Sets fINSTANCE = new Sets(); private static Set names; + protected static Random rnd = new Random(); public static Sets getInstance() { return fINSTANCE; @@ -93,6 +90,24 @@ public class Sets extends HashMap { } return null; } + + public static String findCard(String name, boolean random) { + if (!random) { + return findCard(name); + } else { + List cards = new ArrayList(); + for (ExpansionSet set: fINSTANCE.values()) { + String cardName = set.findCard(name, true); + if (cardName != null) { + cards.add(cardName); + } + } + if (cards.size() > 0) { + return cards.get(rnd.nextInt(cards.size())); + } + } + return null; + } public static ExpansionSet findSet(String code) { for (ExpansionSet set: fINSTANCE.values()) { diff --git a/Mage/src/mage/cards/ExpansionSet.java b/Mage/src/mage/cards/ExpansionSet.java index e4870d6bd1e..6ef19623954 100644 --- a/Mage/src/mage/cards/ExpansionSet.java +++ b/Mage/src/mage/cards/ExpansionSet.java @@ -143,6 +143,19 @@ public abstract class ExpansionSet implements Serializable { return null; } + public String findCard(String name, boolean random) { + List cards = new ArrayList(); + for (Card card: createCards()) { + if (name.equals(card.getName())) { + cards.add(card.getClass().getCanonicalName()); + } + } + if (cards.size() > 0) { + return cards.get(rnd.nextInt(cards.size())); + } + return null; + } + public String findCard(int cardNum) { for (Card card: createCards()) { if (card.getCardNumber() == cardNum) From 5bdfd1b467ca29dc39ce8f5282ea0a9199492a17 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Mon, 27 Dec 2010 00:36:27 +0300 Subject: [PATCH 04/23] [mage.card.plugin] enhanced sorting caching (no blinking on every phase) --- Mage.Client/plugins/mage-card-plugin.jar | Bin 306121 -> 305956 bytes .../java/org/mage/card/arcane/CardPanel.java | 15 ++++----------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/Mage.Client/plugins/mage-card-plugin.jar b/Mage.Client/plugins/mage-card-plugin.jar index 8de1c45eb491f8935e321226e4cbc9f34b8a33b5..9c662a1e057e93a177e217f7d05b299984921ed4 100644 GIT binary patch delta 17036 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 HEy(`^->SP8 delta 17410 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 b^Vx_4h;{^ju2$xo<3DhEi~=WcQltDIciZmX diff --git a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java index 184b4c292b1..aeed5363340 100644 --- a/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java +++ b/Mage.Plugins/Mage.Card.Plugin/src/main/java/org/mage/card/arcane/CardPanel.java @@ -65,9 +65,8 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti static private final float rotCenterToBottomCorner = 0.7071067811865475244008443621048f; public CardView gameCard; - public PermanentView gamePermanent; - public CardPanel attachedToPanel; - public List attachedPanels = new ArrayList(); + //public List attachedPanels = new ArrayList(); + private List links = new ArrayList(); public double tappedAngle = 0; public ScaledImagePanel imagePanel; public ImagePanel overlayPanel; @@ -101,8 +100,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti this.isPermanent = this.gameCard instanceof PermanentView; if (isPermanent) { - this.gamePermanent = (PermanentView) this.gameCard; - this.hasSickness = this.gamePermanent.hasSummoningSickness(); + this.hasSickness = ((PermanentView) this.gameCard).hasSummoningSickness(); } //for container debug (don't remove) @@ -456,12 +454,7 @@ public class CardPanel extends MagePermanent implements MouseListener, MouseMoti @Override public List getLinks() { - List list = new ArrayList(); - if (attachedPanels == null) return list; - for (MagePermanent p : attachedPanels) { - list.add(p); - } - return list; + return links; } @Override From c8db6ba3500e6071a58c9b19da40b0a6e91ca919 Mon Sep 17 00:00:00 2001 From: BetaSteward Date: Sun, 26 Dec 2010 20:41:40 -0500 Subject: [PATCH 05/23] moved {this} and {source} replacement to server --- Mage.Common/src/mage/view/AbilityView.java | 9 +----- Mage.Common/src/mage/view/CardView.java | 30 +++++++++---------- .../src/mage/view/StackAbilityView.java | 9 +----- Mage/src/mage/abilities/Abilities.java | 2 +- Mage/src/mage/abilities/AbilitiesImpl.java | 2 +- Mage/src/mage/abilities/Ability.java | 1 + Mage/src/mage/abilities/AbilityImpl.java | 11 +++++++ .../mage/abilities/TriggeredAbilityImpl.java | 5 +++- .../SacrificeSourceUnlessPaysEffect.java | 18 ++++++----- .../common/TapSourceUnlessPaysEffect.java | 16 +++++----- .../mage/abilities/keyword/LevelAbility.java | 2 +- Mage/src/mage/cards/CardImpl.java | 4 +-- Mage/src/mage/game/stack/StackAbility.java | 5 ++++ 13 files changed, 62 insertions(+), 52 deletions(-) diff --git a/Mage.Common/src/mage/view/AbilityView.java b/Mage.Common/src/mage/view/AbilityView.java index 6e1035101ac..44d73318b96 100644 --- a/Mage.Common/src/mage/view/AbilityView.java +++ b/Mage.Common/src/mage/view/AbilityView.java @@ -48,7 +48,7 @@ public class AbilityView extends CardView { this.sourceName = sourceName; this.sourceCard = sourceCard; this.rules = new ArrayList(); - rules.add(formatRule(ability.getRule())); + rules.add(ability.getRule(sourceName)); this.power = ""; this.toughness = ""; this.loyalty = ""; @@ -60,13 +60,6 @@ public class AbilityView extends CardView { this.art = ""; } - @Override - protected String formatRule(String rule) { - String newRule = rule.replace("{this}", this.sourceName); - newRule.replace("{source}", this.sourceName); - return newRule; - } - public CardView getSourceCard() { return this.sourceCard; } diff --git a/Mage.Common/src/mage/view/CardView.java b/Mage.Common/src/mage/view/CardView.java index 4116c281214..4c733d5cdb5 100644 --- a/Mage.Common/src/mage/view/CardView.java +++ b/Mage.Common/src/mage/view/CardView.java @@ -76,7 +76,7 @@ public class CardView implements Serializable { public CardView(Card card) { this.id = card.getId(); this.name = card.getName(); - this.rules = formatRules(card.getRules()); + this.rules = card.getRules(); if (card instanceof Permanent) { this.power = Integer.toString(card.getPower().getValue()); this.toughness = Integer.toString(card.getToughness().getValue()); @@ -118,7 +118,7 @@ public class CardView implements Serializable { CardView(Token token) { this.id = token.getId(); this.name = token.getName(); - this.rules = formatRules(token.getAbilities().getRules()); + this.rules = token.getAbilities().getRules(this.name); this.power = token.getPower().toString(); this.toughness = token.getToughness().toString(); this.loyalty = token.getLoyalty().toString(); @@ -143,19 +143,19 @@ public class CardView implements Serializable { } } - protected List formatRules(List rules) { - List newRules = new ArrayList(); - for (String rule: rules) { - newRules.add(formatRule(rule)); - } - return newRules; - } - - protected String formatRule(String rule) { - String replace = rule.replace("{this}", this.name); - replace = replace.replace("{source}", this.name); - return replace; - } +// protected List formatRules(List rules) { +// List newRules = new ArrayList(); +// for (String rule: rules) { +// newRules.add(formatRule(rule)); +// } +// return newRules; +// } +// +// protected String formatRule(String rule) { +// String replace = rule.replace("{this}", this.name); +// replace = replace.replace("{source}", this.name); +// return replace; +// } public String getName() { return name; diff --git a/Mage.Common/src/mage/view/StackAbilityView.java b/Mage.Common/src/mage/view/StackAbilityView.java index 5beaed5077a..d795daa7fc2 100644 --- a/Mage.Common/src/mage/view/StackAbilityView.java +++ b/Mage.Common/src/mage/view/StackAbilityView.java @@ -47,7 +47,7 @@ public class StackAbilityView extends CardView { this.sourceName = sourceName; this.sourceCard = sourceCard; this.rules = new ArrayList(); - rules.add(formatRule(ability.getRule())); + rules.add(ability.getRule(sourceName)); this.power = ability.getPower().toString(); this.toughness = ability.getToughness().toString(); this.loyalty = ability.getLoyalty().toString(); @@ -60,13 +60,6 @@ public class StackAbilityView extends CardView { setTargets(ability.getTargets()); } - @Override - protected String formatRule(String rule) { - String newRule = rule.replace("{this}", this.sourceName); - newRule.replace("{source}", this.sourceName); - return newRule; - } - public CardView getSourceCard() { return this.sourceCard; } diff --git a/Mage/src/mage/abilities/Abilities.java b/Mage/src/mage/abilities/Abilities.java index 84fa54f9061..373446b4554 100644 --- a/Mage/src/mage/abilities/Abilities.java +++ b/Mage/src/mage/abilities/Abilities.java @@ -39,7 +39,7 @@ import mage.filter.FilterAbility; public interface Abilities extends List, Serializable { - public List getRules(); + public List getRules(String source); public Abilities getActivatedAbilities(Zone zone); public Abilities getActivatedAbilities(Zone zone, FilterAbility filter); public Abilities getManaAbilities(Zone zone); diff --git a/Mage/src/mage/abilities/AbilitiesImpl.java b/Mage/src/mage/abilities/AbilitiesImpl.java index ae40e84139e..e63ac8d181f 100644 --- a/Mage/src/mage/abilities/AbilitiesImpl.java +++ b/Mage/src/mage/abilities/AbilitiesImpl.java @@ -58,7 +58,7 @@ public class AbilitiesImpl extends ArrayList implements Ab } @Override - public List getRules() { + public List getRules(String source) { List rules = new ArrayList(); for (T ability:this) { diff --git a/Mage/src/mage/abilities/Ability.java b/Mage/src/mage/abilities/Ability.java index ab37daa5358..54c9cc73b22 100644 --- a/Mage/src/mage/abilities/Ability.java +++ b/Mage/src/mage/abilities/Ability.java @@ -73,6 +73,7 @@ public interface Ability extends Serializable { public Zone getZone(); public boolean isUsesStack(); public String getRule(); + public String getRule(String source); public boolean activate(Game game, boolean noMana); public boolean resolve(Game game); public void reset(Game game); diff --git a/Mage/src/mage/abilities/AbilityImpl.java b/Mage/src/mage/abilities/AbilityImpl.java index b8149c48050..fdfe229c7a9 100644 --- a/Mage/src/mage/abilities/AbilityImpl.java +++ b/Mage/src/mage/abilities/AbilityImpl.java @@ -302,6 +302,17 @@ public abstract class AbilityImpl> implements Ability { return sbRule.toString(); } + @Override + public String getRule(String source) { + return formatRule(getRule(), source); + } + + protected String formatRule(String rule, String source) { + String replace = rule.replace("{this}", source); + replace = replace.replace("{source}", source); + return replace; + } + @Override public void addCost(Cost cost) { if (cost != null) { diff --git a/Mage/src/mage/abilities/TriggeredAbilityImpl.java b/Mage/src/mage/abilities/TriggeredAbilityImpl.java index df530369a21..4aa8a015647 100644 --- a/Mage/src/mage/abilities/TriggeredAbilityImpl.java +++ b/Mage/src/mage/abilities/TriggeredAbilityImpl.java @@ -81,10 +81,13 @@ public abstract class TriggeredAbilityImpl> ex Player player = game.getPlayer(this.getControllerId()); MageObject object = game.getObject(sourceId); StringBuilder sb = new StringBuilder(); - sb.append("Use ").append(this.getRule()).append("ability"); if (object != null) { + sb.append("Use ").append(this.getRule(object.getName())).append("ability"); sb.append(" from ").append(object.getName()); } + else { + sb.append("Use ").append(this.getRule()).append("ability"); + } sb.append("?"); if (!player.chooseUse(this.effects.get(0).getOutcome(), sb.toString(), game)) { return false; diff --git a/Mage/src/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java b/Mage/src/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java index b8b48c9a84a..c95b298dd67 100644 --- a/Mage/src/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java +++ b/Mage/src/mage/abilities/effects/common/SacrificeSourceUnlessPaysEffect.java @@ -30,15 +30,17 @@ public class SacrificeSourceUnlessPaysEffect extends OneShotEffect { else sb.append("-").append(level2); sb.append(": ").append(power).append("/").append(toughness).append(" "); - for (String rule: abilities.getRules()) { + for (String rule: abilities.getRules("{this}")) { sb.append(rule).append(" "); } return sb.toString(); diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index be6684ac7bc..499e9161711 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -130,9 +130,9 @@ public abstract class CardImpl> extends MageObjectImpl @Override public List getRules() { - List rules = abilities.getRules(); + List rules = abilities.getRules(this.name); if (cardType.contains(CardType.INSTANT) || cardType.contains(CardType.SORCERY)) { - rules.add(0, getSpellAbility().getRule()); + rules.add(0, getSpellAbility().getRule(this.name)); } return rules; } diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index 2228a4e878d..54ddb3d24be 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -192,6 +192,11 @@ public class StackAbility implements StackObject, Ability { return ability.getRule(); } + @Override + public String getRule(String source) { + return ability.getRule(source); + } + @Override public void setControllerId(UUID controllerId) { this.controllerId = controllerId; From 5df3a6e4882056f5a9e4bb40e7998b8cd00e18cd Mon Sep 17 00:00:00 2001 From: BetaSteward Date: Sun, 26 Dec 2010 20:42:09 -0500 Subject: [PATCH 06/23] removed bad import --- .../src/main/java/mage/client/util/gui/GuiDisplayUtil.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java index 2027dfdede2..312e4966ea4 100644 --- a/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java +++ b/Mage.Client/src/main/java/mage/client/util/gui/GuiDisplayUtil.java @@ -11,7 +11,6 @@ import mage.utils.CardUtil; import mage.view.CardView; import org.jdesktop.swingx.JXPanel; -import sun.plugin.com.event.COMEventHandler; public class GuiDisplayUtil { private static final Font cardNameFont = new Font("Calibri", Font.BOLD, 15); From 33d29bcdaccd3dc973b65067be14bde369dd7b22 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Tue, 28 Dec 2010 13:32:25 +0300 Subject: [PATCH 07/23] fixed bug in pvp phase stops (now based on uuid, not player's name) --- .../mage/client/dialog/JoinTableDialog.java | 1 - .../main/java/mage/client/remote/Client.java | 2 ++ .../java/mage/client/util/GameManager.java | 14 ++++++++++++ .../java/mage/client/util/PhaseManager.java | 22 +++++++++++++------ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java index bd6cc42bf66..8b7fe1c5e7e 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/JoinTableDialog.java @@ -138,7 +138,6 @@ public class JoinTableDialog extends MageDialog { private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed Session session = MageFrame.getSession(); try { - PhaseManager.getInstance().setName(this.newPlayerPanel.getPlayerName()); joined = session.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), Sets.loadDeck(this.newPlayerPanel.getDeckFile())); } catch (Exception ex) { handleError(ex); diff --git a/Mage.Client/src/main/java/mage/client/remote/Client.java b/Mage.Client/src/main/java/mage/client/remote/Client.java index 23623b818ea..d5ee82df5c5 100644 --- a/Mage.Client/src/main/java/mage/client/remote/Client.java +++ b/Mage.Client/src/main/java/mage/client/remote/Client.java @@ -36,6 +36,7 @@ import javax.swing.JOptionPane; import mage.client.MageFrame; import mage.client.chat.ChatPanel; import mage.client.plugins.impl.Plugins; +import mage.client.util.GameManager; import mage.interfaces.callback.CallbackClient; import mage.interfaces.callback.ClientCallback; import mage.util.Logging; @@ -73,6 +74,7 @@ public class Client implements CallbackClient { try { if (callback.getMethod().equals("startGame")) { UUID[] data = (UUID[]) callback.getData(); + GameManager.getInstance().setCurrentPlayerUUID(data[1]); gameStarted(data[0], data[1]); } else if (callback.getMethod().equals("replayGame")) { diff --git a/Mage.Client/src/main/java/mage/client/util/GameManager.java b/Mage.Client/src/main/java/mage/client/util/GameManager.java index c7950a07fcf..047bb4acdc0 100644 --- a/Mage.Client/src/main/java/mage/client/util/GameManager.java +++ b/Mage.Client/src/main/java/mage/client/util/GameManager.java @@ -1,5 +1,7 @@ package mage.client.util; +import java.util.UUID; + /** * Controls game state on client side. * @@ -7,15 +9,27 @@ package mage.client.util; */ public class GameManager { private static GameManager fInstance = new GameManager(); + public static GameManager getInstance() { return fInstance; } + public void setStackSize(int stackSize) { this.stackSize = stackSize; } + public int getStackSize() { return stackSize; } + public UUID getCurrentPlayerUUID() { + return currentPlayerUUID; + } + + public void setCurrentPlayerUUID(UUID currentPlayerUUID) { + this.currentPlayerUUID = currentPlayerUUID; + } + private int stackSize; + private UUID currentPlayerUUID; } diff --git a/Mage.Client/src/main/java/mage/client/util/PhaseManager.java b/Mage.Client/src/main/java/mage/client/util/PhaseManager.java index 15e357d3fcc..7fee7768b36 100644 --- a/Mage.Client/src/main/java/mage/client/util/PhaseManager.java +++ b/Mage.Client/src/main/java/mage/client/util/PhaseManager.java @@ -2,9 +2,11 @@ package mage.client.util; import mage.client.MageFrame; import mage.view.GameView; +import mage.view.PlayerView; import java.util.HashMap; import java.util.Map; +import java.util.UUID; import java.util.prefs.Preferences; public class PhaseManager { @@ -51,21 +53,27 @@ public class PhaseManager { put("End Turn - play instants and activated abilities.", END_OF_TURN_OTHERS); }}; - private String yourName; - public static PhaseManager getInstance() { return fInstance; } - public void setName(String yourName) { - this.yourName = yourName; - } - public boolean isSkip(GameView gameView, String message) { if (GameManager.getInstance().getStackSize() > 0) { return false; } - Map map = gameView.getActivePlayerName().equals(DEFAULT_PLAYER_NAME) ? mapYou : mapOthers; + UUID activePlayer = null; + Map map = mapOthers; + for (PlayerView playerView : gameView.getPlayers()) { + if (playerView.isActive()) { + activePlayer = playerView.getPlayerId(); + if (activePlayer.equals(GameManager.getInstance().getCurrentPlayerUUID())) { + map = mapYou; + } + } + } + if (activePlayer == null) { + throw new IllegalStateException("No active player found."); + } for (Map.Entry entry : map.entrySet()) { if (message.equals(entry.getKey())) { Preferences prefs = MageFrame.getPreferences(); From 10f365254d9249757ca28ef1067e9561285018a8 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Tue, 28 Dec 2010 15:40:28 +0300 Subject: [PATCH 08/23] MageFrame code cleanup. --- .../src/main/java/mage/client/MageFrame.java | 139 ++++++++++-------- 1 file changed, 76 insertions(+), 63 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 966e16792ac..a832d825eae 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -138,71 +138,13 @@ public class MageFrame extends javax.swing.JFrame { desktopPane.add(pickNumber, JLayeredPane.POPUP_LAYER); session.getUI().addComponent(MageComponents.DESKTOP_PANE, desktopPane); - final JEditorPane cardInfoPane = (JEditorPane) Plugins.getInstance().getCardInfoPane(); - cardInfoPane.setSize(320, 201); - cardInfoPane.setLocation(40, 40); - cardInfoPane.setBackground(new Color(0,0,0,0)); - - MageRoundPane popupContainer = new MageRoundPane(); - popupContainer.setLayout(null); - - popupContainer.add(cardInfoPane); - popupContainer.setVisible(false); - popupContainer.setBounds(0, 0, 320 + 80, 201 + 80); - - desktopPane.add(popupContainer, JLayeredPane.POPUP_LAYER); - - session.getUI().addComponent(MageComponents.CARD_INFO_PANE, cardInfoPane); - session.getUI().addComponent(MageComponents.POPUP_CONTAINER, popupContainer); - ManaSymbols.loadImages(); - String filename = "/background.jpg"; - try { - if (Plugins.getInstance().isThemePluginLoaded()) { - Map ui = new HashMap(); - backgroundPane = (ImagePanel) Plugins.getInstance().updateTablePanel(ui); - } else { - InputStream is = this.getClass().getResourceAsStream(filename); - BufferedImage background = ImageIO.read(is); - backgroundPane = new ImagePanel(background, ImagePanel.SCALED); - } - backgroundPane.setSize(1024, 768); - desktopPane.add(backgroundPane, JLayeredPane.DEFAULT_LAYER); - } catch (IOException e) { - e.printStackTrace(); - } - - filename = "/label-mage.png"; - try { - InputStream is = this.getClass().getResourceAsStream(filename); - - float ratio = 1179.0f / 678.0f; - titleRectangle = new Rectangle(640, (int)(640 / ratio)); - if (is != null) { - BufferedImage image = ImageIO.read(is); - //ImageIcon resized = new ImageIcon(image.getScaledInstance(titleRectangle.width, titleRectangle.height, java.awt.Image.SCALE_SMOOTH)); - title = new JLabel(); - title.setIcon(new ImageIcon(image)); - backgroundPane.setLayout(null); - backgroundPane.add(title); - } - } catch (IOException e) { - e.printStackTrace(); - } - - filename = "/icon-mage.png"; - try { - InputStream is = this.getClass().getResourceAsStream(filename); - - if (is != null) { - BufferedImage image = ImageIO.read(is); - setIconImage(image); - } - } catch (IOException e) { - e.printStackTrace(); - } - + addTooltipContainer(); + setBackground(); + addMageLabel(); + setAppIcon(); + desktopPane.add(ArrowBuilder.getArrowsPanel(), JLayeredPane.DRAG_LAYER); desktopPane.addComponentListener(new ComponentAdapter(){ @@ -273,6 +215,77 @@ public class MageFrame extends javax.swing.JFrame { } }); } + + private void addTooltipContainer() { + final JEditorPane cardInfoPane = (JEditorPane) Plugins.getInstance().getCardInfoPane(); + cardInfoPane.setSize(320, 201); + cardInfoPane.setLocation(40, 40); + cardInfoPane.setBackground(new Color(0,0,0,0)); + + MageRoundPane popupContainer = new MageRoundPane(); + popupContainer.setLayout(null); + + popupContainer.add(cardInfoPane); + popupContainer.setVisible(false); + popupContainer.setBounds(0, 0, 320 + 80, 201 + 80); + + desktopPane.add(popupContainer, JLayeredPane.POPUP_LAYER); + + session.getUI().addComponent(MageComponents.CARD_INFO_PANE, cardInfoPane); + session.getUI().addComponent(MageComponents.POPUP_CONTAINER, popupContainer); + } + + private void setBackground() { + String filename = "/background.jpg"; + try { + if (Plugins.getInstance().isThemePluginLoaded()) { + Map ui = new HashMap(); + backgroundPane = (ImagePanel) Plugins.getInstance().updateTablePanel(ui); + } else { + InputStream is = this.getClass().getResourceAsStream(filename); + BufferedImage background = ImageIO.read(is); + backgroundPane = new ImagePanel(background, ImagePanel.SCALED); + } + backgroundPane.setSize(1024, 768); + desktopPane.add(backgroundPane, JLayeredPane.DEFAULT_LAYER); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void addMageLabel() { + String filename = "/label-mage.png"; + try { + InputStream is = this.getClass().getResourceAsStream(filename); + + float ratio = 1179.0f / 678.0f; + titleRectangle = new Rectangle(640, (int)(640 / ratio)); + if (is != null) { + BufferedImage image = ImageIO.read(is); + //ImageIcon resized = new ImageIcon(image.getScaledInstance(titleRectangle.width, titleRectangle.height, java.awt.Image.SCALE_SMOOTH)); + title = new JLabel(); + title.setIcon(new ImageIcon(image)); + backgroundPane.setLayout(null); + backgroundPane.add(title); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void setAppIcon() { + String filename = "/icon-mage.png"; + try { + InputStream is = this.getClass().getResourceAsStream(filename); + + if (is != null) { + BufferedImage image = ImageIO.read(is); + setIconImage(image); + } + } catch (IOException e) { + e.printStackTrace(); + } + } private void btnImagesActionPerformed(java.awt.event.ActionEvent evt) { Plugins.getInstance().downloadImage(CardsStorage.getAllCards()); From 736630cfd1259d186dd6162f1cc46bce901e366a Mon Sep 17 00:00:00 2001 From: Loki Date: Tue, 28 Dec 2010 20:23:47 +0200 Subject: [PATCH 09/23] Elspeth Tirel --- .../sets/scarsofmirrodin/ElspethTirel.java | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/ElspethTirel.java diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/ElspethTirel.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/ElspethTirel.java new file mode 100644 index 00000000000..069549ed152 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/ElspethTirel.java @@ -0,0 +1,133 @@ +/* + * 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.scarsofmirrodin; + +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.LoyaltyAbility; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.cards.CardImpl; +import mage.filter.common.FilterCreaturePermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.game.permanent.PermanentToken; +import mage.game.permanent.token.SoldierToken; +import mage.players.Player; + +/** + * + * @author Loki + */ +public class ElspethTirel extends CardImpl { + + public ElspethTirel (UUID ownerId) { + super(ownerId, 6, "Elspeth Tirel", Rarity.MYTHIC, new CardType[]{CardType.PLANESWALKER}, "{3}{W}{W}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Elspeth"); + this.color.setWhite(true); + this.loyalty = new MageInt(4); + this.addAbility(new LoyaltyAbility(new ElspethTirelFirstEffect(), 2)); + this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new SoldierToken(), 3), -2)); + this.addAbility(new LoyaltyAbility(new ElspethTirelThirdEffect(), -5)); + } + + public ElspethTirel (final ElspethTirel card) { + super(card); + } + + @Override + public ElspethTirel copy() { + return new ElspethTirel(this); + } +} + +class ElspethTirelFirstEffect extends OneShotEffect { + public ElspethTirelFirstEffect() { + super(Constants.Outcome.GainLife); + } + + public ElspethTirelFirstEffect(final ElspethTirelFirstEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + int amount = game.getBattlefield().countAll(new FilterCreaturePermanent(), source.getControllerId()); + Player player = game.getPlayer(source.getControllerId()); + if (player != null) { + player.gainLife(amount, game); + } + return true; + } + + @Override + public ElspethTirelFirstEffect copy() { + return new ElspethTirelFirstEffect(this); + } + + @Override + public String getText(Ability source) { + return "You gain 1 life for each creature you control"; + } +} + +class ElspethTirelThirdEffect extends OneShotEffect { + public ElspethTirelThirdEffect() { + super(Constants.Outcome.DestroyPermanent); + } + + public ElspethTirelThirdEffect(final ElspethTirelThirdEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + for (Permanent perm: game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { + if (!perm.getId().equals(source.getSourceId()) && !(perm instanceof PermanentToken) && ! (perm.getCardType().contains(CardType.LAND))) + perm.destroy(source.getId(), game, false); + } + return true; + } + + @Override + public ElspethTirelThirdEffect copy() { + return new ElspethTirelThirdEffect(this); + } + + @Override + public String getText(Ability source) { + return "Destroy all other permanents except for lands and tokens"; + } +} \ No newline at end of file From ff669009af2f9a0d53c86d92318c5f8b8dc02492 Mon Sep 17 00:00:00 2001 From: Loki Date: Tue, 28 Dec 2010 22:40:46 +0200 Subject: [PATCH 10/23] add more generic WhileConditionContiniusEffect and Wurmcoil Engine --- .../sets/scarsofmirrodin/WurmcoilEngine.java | 96 +++++++++++++++++++ .../condition/common/ControlsPermanent.java | 19 ++++ .../WhileConditionContiniousEffect.java | 32 +++++++ .../WhileControlsContinuousEffect.java | 69 ------------- .../BoostSourceWhileControlsEffect.java | 13 ++- 5 files changed, 156 insertions(+), 73 deletions(-) create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java create mode 100644 Mage/src/mage/abilities/condition/common/ControlsPermanent.java create mode 100644 Mage/src/mage/abilities/effects/WhileConditionContiniousEffect.java delete mode 100644 Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java new file mode 100644 index 00000000000..b755693d3c9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/WurmcoilEngine.java @@ -0,0 +1,96 @@ +/* + * 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.scarsofmirrodin; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.ObjectColor; +import mage.abilities.Ability; +import mage.abilities.common.PutIntoGraveFromBattlefieldTriggeredAbility; +import mage.abilities.effects.common.CreateTokenEffect; +import mage.abilities.keyword.DeathtouchAbility; +import mage.abilities.keyword.LifelinkAbility; +import mage.cards.CardImpl; +import mage.game.permanent.token.Token; + +/** + * + * @author Loki + */ +public class WurmcoilEngine extends CardImpl { + + public WurmcoilEngine (UUID ownerId) { + super(ownerId, 223, "Wurmcoil Engine", Rarity.MYTHIC, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{6}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Wurm"); + this.power = new MageInt(6); + this.toughness = new MageInt(6); + this.addAbility(DeathtouchAbility.getInstance()); + this.addAbility(LifelinkAbility.getInstance()); + Ability ability = new PutIntoGraveFromBattlefieldTriggeredAbility(new CreateTokenEffect(new Wurm1Token()), false); + ability.addEffect(new CreateTokenEffect(new Wurm2Token())); + this.addAbility(ability); + } + + public WurmcoilEngine (final WurmcoilEngine card) { + super(card); + } + + @Override + public WurmcoilEngine copy() { + return new WurmcoilEngine(this); + } + +} + +class Wurm1Token extends Token { + public Wurm1Token() { + super("Wurm", "a 3/3 colorless Wurm artifact creature token with deathtouch"); + cardType.add(CardType.CREATURE); + subtype.add("Wurm"); + power = new MageInt(3); + toughness = new MageInt(3); + this.addAbility(DeathtouchAbility.getInstance()); + } +} + +class Wurm2Token extends Token { + public Wurm2Token() { + super("Wurm", "a 3/3 colorless Wurm artifact creature token with lifelink"); + cardType.add(CardType.CREATURE); + subtype.add("Wurm"); + power = new MageInt(3); + toughness = new MageInt(3); + this.addAbility(LifelinkAbility.getInstance()); + } +} \ No newline at end of file diff --git a/Mage/src/mage/abilities/condition/common/ControlsPermanent.java b/Mage/src/mage/abilities/condition/common/ControlsPermanent.java new file mode 100644 index 00000000000..7c35d6af7ed --- /dev/null +++ b/Mage/src/mage/abilities/condition/common/ControlsPermanent.java @@ -0,0 +1,19 @@ +package mage.abilities.condition.common; + +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.filter.FilterPermanent; +import mage.game.Game; + +public class ControlsPermanent implements Condition { + private FilterPermanent filter; + + public ControlsPermanent(FilterPermanent filter) { + this.filter = filter; + } + + @Override + public boolean apply(Game game, Ability source) { + return game.getBattlefield().countAll(filter, source.getControllerId()) > 0; + } +} diff --git a/Mage/src/mage/abilities/effects/WhileConditionContiniousEffect.java b/Mage/src/mage/abilities/effects/WhileConditionContiniousEffect.java new file mode 100644 index 00000000000..f5b8e997406 --- /dev/null +++ b/Mage/src/mage/abilities/effects/WhileConditionContiniousEffect.java @@ -0,0 +1,32 @@ +package mage.abilities.effects; + +import mage.Constants; +import mage.abilities.Ability; +import mage.abilities.condition.Condition; +import mage.game.Game; + +public abstract class WhileConditionContiniousEffect> extends ContinuousEffectImpl { + protected Condition condition; + + public WhileConditionContiniousEffect(Constants.Duration duration, Constants.Layer layer, Constants.SubLayer sublayer, Condition condition, Constants.Outcome outcome) { + super(duration, outcome); + this.condition = condition; + this.layer = layer; + this.sublayer = sublayer; + } + + public WhileConditionContiniousEffect(final WhileConditionContiniousEffect effect) { + super(effect); + this.condition = effect.condition; + } + + @Override + public boolean apply(Game game, Ability source) { + if (condition.apply(game, source)) { + return applyEffect(game, source); + } + return false; + } + + protected abstract boolean applyEffect(Game game, Ability source); +} diff --git a/Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java b/Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java deleted file mode 100644 index 07a24ffe5ef..00000000000 --- a/Mage/src/mage/abilities/effects/WhileControlsContinuousEffect.java +++ /dev/null @@ -1,69 +0,0 @@ -/* -* 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; - -import mage.Constants.Duration; -import mage.Constants.Layer; -import mage.Constants.Outcome; -import mage.Constants.SubLayer; -import mage.abilities.Ability; -import mage.filter.FilterPermanent; -import mage.game.Game; - -/** - * - * @author BetaSteward_at_googlemail.com - */ -public abstract class WhileControlsContinuousEffect> extends ContinuousEffectImpl { - - protected FilterPermanent filter; - - public WhileControlsContinuousEffect(Duration duration, Layer layer, SubLayer sublayer, FilterPermanent filter, Outcome outcome) { - super(duration, outcome); - this.filter = filter; - this.layer = layer; - this.sublayer = sublayer; - } - - public WhileControlsContinuousEffect(WhileControlsContinuousEffect effect) { - super(effect); - this.filter = effect.filter.copy(); - } - - @Override - public boolean apply(Game game, Ability source) { - if (game.getBattlefield().countAll(filter, source.getControllerId()) > 0) { - return applyEffect(game, source); - } - return false; - } - - protected abstract boolean applyEffect(Game game, Ability source); - -} diff --git a/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java b/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java index 845be8d499b..94e74209f34 100644 --- a/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java +++ b/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java @@ -28,7 +28,8 @@ package mage.abilities.effects.common; -import mage.abilities.effects.WhileControlsContinuousEffect; +import mage.abilities.condition.common.ControlsPermanent; +import mage.abilities.effects.WhileConditionContiniousEffect; import mage.Constants.Duration; import mage.Constants.Layer; import mage.Constants.Outcome; @@ -38,19 +39,23 @@ import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.List; + /** * * @author BetaSteward_at_googlemail.com */ -public class BoostSourceWhileControlsEffect extends WhileControlsContinuousEffect { +public class BoostSourceWhileControlsEffect extends WhileConditionContiniousEffect { private int power; private int toughness; + private List filterDescription; public BoostSourceWhileControlsEffect(FilterPermanent filter, int power, int toughness) { - super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, filter, Outcome.BoostCreature); + super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, new ControlsPermanent(filter), Outcome.BoostCreature); this.power = power; this.toughness = toughness; + this.filterDescription = filter.getName(); } public BoostSourceWhileControlsEffect(final BoostSourceWhileControlsEffect effect) { @@ -76,6 +81,6 @@ public class BoostSourceWhileControlsEffect extends WhileControlsContinuousEffec @Override public String getText(Ability source) { - return "{this} gets " + String.format("%1$+d/%2$+d", power, toughness) + " as long as you control a " + filter.getName(); + return "{this} gets " + String.format("%1$+d/%2$+d", power, toughness) + " as long as you control a " + filterDescription; } } From aa5e45e69d8a0ad8056bf81e6cc752101d5f5030 Mon Sep 17 00:00:00 2001 From: Loki Date: Tue, 28 Dec 2010 22:45:09 +0200 Subject: [PATCH 11/23] description cloning --- .../effects/common/BoostSourceWhileControlsEffect.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java b/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java index 94e74209f34..1abdc1905c0 100644 --- a/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java +++ b/Mage/src/mage/abilities/effects/common/BoostSourceWhileControlsEffect.java @@ -39,6 +39,7 @@ import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; +import java.util.ArrayList; import java.util.List; /** @@ -62,6 +63,8 @@ public class BoostSourceWhileControlsEffect extends WhileConditionContiniousEffe super(effect); this.power = effect.power; this.toughness = effect.toughness; + this.filterDescription = new ArrayList(); + this.filterDescription.addAll(effect.filterDescription); } @Override From 4b26727d71355171711eb5cf4c49324767667922 Mon Sep 17 00:00:00 2001 From: BetaSteward Date: Tue, 28 Dec 2010 23:34:06 -0500 Subject: [PATCH 12/23] changes to support multi-duel matches and sideboarding --- .../src/main/java/mage/client/MageFrame.java | 12 ++- .../java/mage/client/constants/Constants.java | 7 ++ .../client/deckeditor/DeckEditorPane.form | 2 +- .../client/deckeditor/DeckEditorPane.java | 7 +- .../client/deckeditor/DeckEditorPanel.java | 41 +++++++-- .../mage/client/dialog/NewTableDialog.form | 87 +++++++++++------- .../mage/client/dialog/NewTableDialog.java | 85 +++++++++++------ .../main/java/mage/client/remote/Client.java | 17 +++- .../main/java/mage/client/remote/Session.java | 17 +++- Mage.Common/src/mage/interfaces/Server.java | 1 + .../src/mage/view/TableClientMessage.java | 71 ++++++++++++++ .../src/mage/game/TwoPlayerDuel.java | 4 +- .../src/mage/player/ai/ComputerPlayer.java | 9 +- .../src/mage/player/human/HumanPlayer.java | 11 ++- Mage.Server/plugins/mage-deck-constructed.jar | Bin 3165 -> 3166 bytes Mage.Server/plugins/mage-deck-limited.jar | Bin 2391 -> 2391 bytes Mage.Server/plugins/mage-game-freeforall.jar | Bin 5303 -> 5305 bytes .../plugins/mage-game-twoplayerduel.jar | Bin 4838 -> 4854 bytes Mage.Server/plugins/mage-player-ai.jar | Bin 28914 -> 29003 bytes Mage.Server/plugins/mage-player-aiminimax.jar | Bin 36468 -> 36469 bytes Mage.Server/plugins/mage-player-human.jar | Bin 11206 -> 11286 bytes .../src/main/java/mage/server/ServerImpl.java | 19 +++- .../src/main/java/mage/server/Session.java | 8 +- .../java/mage/server/game/GameController.java | 6 +- .../java/mage/server/game/GameManager.java | 4 +- .../java/mage/server/game/GameWorker.java | 7 +- .../main/java/mage/server/game/GamesRoom.java | 3 - .../java/mage/server/game/GamesRoomImpl.java | 3 - .../mage/server/game/TableController.java | 79 +++++++++++++--- .../java/mage/server/game/TableManager.java | 8 +- Mage/src/mage/game/Game.java | 4 +- Mage/src/mage/game/GameImpl.java | 41 +++++---- Mage/src/mage/game/Table.java | 34 ++++++- Mage/src/mage/game/events/TableEvent.java | 20 +++- .../mage/game/events/TableEventSource.java | 11 ++- Mage/src/mage/game/match/Match.java | 6 +- Mage/src/mage/game/match/MatchImpl.java | 38 ++++++++ Mage/src/mage/game/match/MatchPlayer.java | 15 +++ Mage/src/mage/players/Player.java | 3 + Mage/src/mage/players/PlayerImpl.java | 3 + 40 files changed, 536 insertions(+), 147 deletions(-) create mode 100644 Mage.Common/src/mage/view/TableClientMessage.java diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index a832d825eae..91c5bb73ed5 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -54,11 +54,13 @@ import javax.swing.*; import javax.swing.JToolBar.Separator; import com.sun.java.swing.Painter; +import mage.cards.decks.Deck; import mage.client.cards.CardsStorage; import mage.client.components.MageComponents; import mage.client.components.MageJDesktop; import mage.client.components.MageRoundPane; import mage.client.components.arcane.ManaSymbols; +import mage.client.constants.Constants.DeckEditorMode; import mage.client.dialog.*; import mage.client.plugins.impl.Plugins; import mage.client.remote.Session; @@ -502,11 +504,10 @@ public class MageFrame extends javax.swing.JFrame { private void btnDeckEditorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeckEditorActionPerformed this.gamePane.setVisible(false); this.tablesPane.setVisible(false); - this.deckEditorPane.setVisible(true); - this.deckEditorPane.showTables(); + showDeckEditor(DeckEditorMode.Constructed, null, null); }//GEN-LAST:event_btnDeckEditorActionPerformed - private void btnPreferencesActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeckEditorActionPerformed + private void btnPreferencesActionPerformed(java.awt.event.ActionEvent evt) { PhasesDialog.main(new String[]{}); } @@ -562,6 +563,11 @@ public class MageFrame extends javax.swing.JFrame { this.deckEditorPane.setVisible(false); } + public void showDeckEditor(DeckEditorMode mode, Deck deck, UUID tableId) { + this.deckEditorPane.setVisible(true); + this.deckEditorPane.show(mode, deck, tableId); + } + public static CombatDialog getCombatDialog() { return combat; } diff --git a/Mage.Client/src/main/java/mage/client/constants/Constants.java b/Mage.Client/src/main/java/mage/client/constants/Constants.java index 2804f8d751b..a91617ed202 100644 --- a/Mage.Client/src/main/java/mage/client/constants/Constants.java +++ b/Mage.Client/src/main/java/mage/client/constants/Constants.java @@ -82,4 +82,11 @@ public final class Constants { public static final String imageBaseDir = "plugins" + File.separator + "images" + File.separator; public static final String IMAGE_PROPERTIES_FILE = "image.url.properties"; } + + public enum DeckEditorMode { + Constructed, + Limited, + Sideboard + } + } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.form b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.form index 69dff9f90b4..6f2e6897658 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.form +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.form @@ -1,4 +1,4 @@ - +
    diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java index 3a39ea23f7e..2d0b9ba46b1 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java @@ -37,10 +37,13 @@ package mage.client.deckeditor; import java.awt.Component; import java.util.HashMap; import java.util.Map; +import java.util.UUID; import javax.swing.JComponent; +import mage.cards.decks.Deck; import mage.client.MagePane; +import mage.client.constants.Constants.DeckEditorMode; import mage.client.plugins.impl.Plugins; /** @@ -70,8 +73,8 @@ public class DeckEditorPane extends MagePane { } } - public void showTables() { - this.deckEditorPanel1.showDeckEditor(); + public void show(DeckEditorMode mode, Deck deck, UUID tableId) { + this.deckEditorPanel1.showDeckEditor(mode, deck, tableId); this.repaint(); } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index 796d54fa3ce..dc571f9ef3b 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -36,7 +36,6 @@ package mage.client.deckeditor; import mage.cards.Card; import mage.cards.decks.Deck; -import mage.cards.decks.DeckCardLists; import mage.client.MageFrame; import mage.client.plugins.impl.Plugins; import mage.client.util.Event; @@ -49,14 +48,13 @@ import mage.view.CardsView; import javax.swing.*; import javax.swing.filechooser.FileFilter; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; +import mage.client.constants.Constants.DeckEditorMode; import mage.sets.Sets; /** @@ -69,6 +67,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { private JFileChooser fcImportDeck; private Deck deck = new Deck(); private boolean isShowCardInfo = false; + private UUID tableId; /** Creates new form DeckEditorPanel */ public DeckEditorPanel() { @@ -85,10 +84,18 @@ public class DeckEditorPanel extends javax.swing.JPanel { jSplitPane1.setOpaque(false); } - public void showDeckEditor() { + public void showDeckEditor(DeckEditorMode mode, Deck deck, UUID tableId) { + if (deck != null) + this.deck = deck; + this.tableId = tableId; + showDeckEditor(mode); + } + + public void showDeckEditor(DeckEditorMode mode) { this.cardSelector.loadCards(this.bigCard); this.cardSelector.setVisible(true); this.jPanel1.setVisible(true); + this.btnSubmit.setVisible(mode == DeckEditorMode.Sideboard); this.cardSelector.getCardsList().clearCardEventListeners(); this.cardSelector.getCardsList().addCardEventListener( new Listener () { @@ -132,6 +139,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { } } ); + refreshDeck(); this.setVisible(true); this.repaint(); } @@ -172,6 +180,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { btnNew = new javax.swing.JButton(); btnExit = new javax.swing.JButton(); btnImport = new javax.swing.JButton(); + btnSubmit = new javax.swing.JButton(); jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); jSplitPane1.setResizeWeight(0.5); @@ -229,7 +238,15 @@ public class DeckEditorPanel extends javax.swing.JPanel { } }); - javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + btnSubmit.setText("Submit"); + btnSubmit.setName("btnSubmit"); // NOI18N + btnSubmit.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + btnSubmitActionPerformed(evt); + } + }); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -253,7 +270,9 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addComponent(btnExit)) .addGroup(jPanel1Layout.createSequentialGroup() .addContainerGap() - .addComponent(btnImport))) + .addComponent(btnImport) + .addContainerGap() + .addComponent(btnSubmit))) .addContainerGap()) ); jPanel1Layout.setVerticalGroup( @@ -270,7 +289,9 @@ public class DeckEditorPanel extends javax.swing.JPanel { .addComponent(btnNew) .addComponent(btnExit)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(btnImport) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(btnImport) + .addComponent(btnSubmit)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, isShowCardInfo ? 30 : 159, Short.MAX_VALUE) .addComponent(cardInfoPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(bigCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) @@ -383,6 +404,11 @@ public class DeckEditorPanel extends javax.swing.JPanel { fcImportDeck.setSelectedFile(null); }//GEN-LAST:event_btnImportActionPerformed + private void btnSubmitActionPerformed(java.awt.event.ActionEvent evt) { + MageFrame.getSession().submitDeck(tableId, deck.getDeckCardLists()); + this.setVisible(false); + } + public DeckImporter getDeckImporter(String file) { if (file.toLowerCase().endsWith("dec")) return new DecDeckImporter(); @@ -410,6 +436,7 @@ public class DeckEditorPanel extends javax.swing.JPanel { // End of variables declaration//GEN-END:variables private JComponent cardInfoPane; + private javax.swing.JButton btnSubmit; } class DeckFilter extends FileFilter { diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form index 2d483f7a68d..8e8ddc4d639 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.form @@ -23,7 +23,7 @@ - + @@ -32,7 +32,7 @@ - + @@ -41,17 +41,22 @@ - + - + - + - + - + + + + + + @@ -61,34 +66,34 @@ - + - + - + - + - + - + @@ -105,25 +110,32 @@ - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + - + - + @@ -132,21 +144,21 @@ - + - + - + - + @@ -260,5 +272,18 @@ + + + + + + + + + + + + + diff --git a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java index 09d9c8f9e55..557e4ea3420 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/NewTableDialog.java @@ -78,6 +78,7 @@ public class NewTableDialog extends MageDialog { /** Creates new form NewTableDialog */ public NewTableDialog() { initComponents(); + this.spnNumWins.setModel(new SpinnerNumberModel(1, 1, 5, 1)); } /** This method is called from within the constructor to @@ -108,6 +109,8 @@ public class NewTableDialog extends MageDialog { jLabel1 = new javax.swing.JLabel(); jSeparator3 = new javax.swing.JSeparator(); jLabel2 = new javax.swing.JLabel(); + lblNumWins = new javax.swing.JLabel(); + spnNumWins = new javax.swing.JSpinner(); setTitle("New Table"); @@ -158,12 +161,21 @@ public class NewTableDialog extends MageDialog { jLabel2.setFont(new java.awt.Font("Tahoma", 1, 11)); jLabel2.setText("Other Players"); + lblNumWins.setLabelFor(spnNumWins); + lblNumWins.setText("Wins"); + + spnNumWins.addChangeListener(new javax.swing.event.ChangeListener() { + public void stateChanged(javax.swing.event.ChangeEvent evt) { + spnNumWinsnumPlayersChanged(evt); + } + }); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap(358, Short.MAX_VALUE) + .addContainerGap(395, Short.MAX_VALUE) .addComponent(btnOK) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(btnCancel) @@ -171,7 +183,7 @@ public class NewTableDialog extends MageDialog { .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(lbDeckType) - .addContainerGap(426, Short.MAX_VALUE)) + .addContainerGap(463, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) @@ -179,43 +191,47 @@ public class NewTableDialog extends MageDialog { .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblNumPlayers) - .addComponent(spnNumPlayers, javax.swing.GroupLayout.PREFERRED_SIZE, 90, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(18, 18, 18) + .addComponent(spnNumPlayers, javax.swing.GroupLayout.PREFERRED_SIZE, 57, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblRange) - .addComponent(cbRange, javax.swing.GroupLayout.PREFERRED_SIZE, 141, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(18, 18, 18) + .addComponent(cbRange, javax.swing.GroupLayout.PREFERRED_SIZE, 143, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(lblAttack) - .addComponent(cbAttackOption, javax.swing.GroupLayout.PREFERRED_SIZE, 199, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(cbAttackOption, 0, 235, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(lblNumWins))) .addComponent(lblGameType) .addComponent(cbDeckType, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap() - .addComponent(jSeparator2, javax.swing.GroupLayout.DEFAULT_SIZE, 466, Short.MAX_VALUE) + .addComponent(jSeparator2, javax.swing.GroupLayout.DEFAULT_SIZE, 503, Short.MAX_VALUE) .addContainerGap()) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(jLabel1) - .addContainerGap(396, Short.MAX_VALUE)) + .addContainerGap(433, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(player1Panel, javax.swing.GroupLayout.DEFAULT_SIZE, 466, Short.MAX_VALUE) + .addComponent(player1Panel, javax.swing.GroupLayout.DEFAULT_SIZE, 503, Short.MAX_VALUE) .addContainerGap()) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(jLabel2) - .addContainerGap(399, Short.MAX_VALUE)) + .addContainerGap(436, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 466, Short.MAX_VALUE) + .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 503, Short.MAX_VALUE) .addContainerGap()) .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.PREFERRED_SIZE, 486, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(jSeparator3, javax.swing.GroupLayout.DEFAULT_SIZE, 466, Short.MAX_VALUE) + .addComponent(jSeparator3, javax.swing.GroupLayout.DEFAULT_SIZE, 503, Short.MAX_VALUE) .addContainerGap())) ); layout.setVerticalGroup( @@ -230,19 +246,24 @@ public class NewTableDialog extends MageDialog { .addGap(0, 0, 0) .addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createSequentialGroup() + .addComponent(lblNumPlayers) + .addGap(0, 0, 0) + .addComponent(spnNumPlayers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(lblRange) + .addGap(0, 0, 0) + .addComponent(cbRange, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addComponent(lblAttack) + .addGap(0, 0, 0) + .addComponent(cbAttackOption, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addGroup(layout.createSequentialGroup() - .addComponent(lblAttack) + .addComponent(lblNumWins) .addGap(0, 0, 0) - .addComponent(cbAttackOption, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblNumPlayers) - .addGap(0, 0, 0) - .addComponent(spnNumPlayers, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRange) - .addGap(0, 0, 0) - .addComponent(cbRange, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(spnNumWins, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -252,19 +273,19 @@ public class NewTableDialog extends MageDialog { .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 83, Short.MAX_VALUE) + .addComponent(pnlOtherPlayers, javax.swing.GroupLayout.DEFAULT_SIZE, 97, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 4, Short.MAX_VALUE) + .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 19, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(btnCancel) .addComponent(btnOK)) - .addGap(0, 0, 0)) + .addGap(6, 6, 6)) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(223, 223, 223) .addComponent(jSeparator3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(149, Short.MAX_VALUE))) + .addContainerGap(159, Short.MAX_VALUE))) ); pack(); @@ -286,7 +307,7 @@ public class NewTableDialog extends MageDialog { options.setDeckType((String) this.cbDeckType.getSelectedItem()); options.setAttackOption((MultiplayerAttackOption) this.cbAttackOption.getSelectedItem()); options.setRange((RangeOfInfluence) this.cbRange.getSelectedItem()); - options.setWinsNeeded(1); + options.setWinsNeeded((Integer)this.spnNumWins.getValue()); table = session.createTable(roomId, options); try { if (session.joinTable(roomId, table.getTableId(), this.player1Panel.getPlayerName(), Sets.loadDeck(this.player1Panel.getDeckFile()))) { @@ -324,6 +345,10 @@ public class NewTableDialog extends MageDialog { createPlayers(numPlayers); }//GEN-LAST:event_numPlayersChanged + private void spnNumWinsnumPlayersChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spnNumWinsnumPlayersChanged + // TODO add your handling code here: + }//GEN-LAST:event_spnNumWinsnumPlayersChanged + private void setGameOptions() { GameTypeView gameType = (GameTypeView) cbGameType.getSelectedItem(); this.spnNumPlayers.setModel(new SpinnerNumberModel(gameType.getMinPlayers(), gameType.getMinPlayers(), gameType.getMaxPlayers(), 1)); @@ -422,10 +447,12 @@ public class NewTableDialog extends MageDialog { private javax.swing.JLabel lblAttack; private javax.swing.JLabel lblGameType; private javax.swing.JLabel lblNumPlayers; + private javax.swing.JLabel lblNumWins; private javax.swing.JLabel lblRange; private mage.client.table.NewPlayerPanel player1Panel; private javax.swing.JPanel pnlOtherPlayers; private javax.swing.JSpinner spnNumPlayers; + private javax.swing.JSpinner spnNumWins; // End of variables declaration//GEN-END:variables } diff --git a/Mage.Client/src/main/java/mage/client/remote/Client.java b/Mage.Client/src/main/java/mage/client/remote/Client.java index d5ee82df5c5..37cde55fadb 100644 --- a/Mage.Client/src/main/java/mage/client/remote/Client.java +++ b/Mage.Client/src/main/java/mage/client/remote/Client.java @@ -33,8 +33,10 @@ import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JOptionPane; +import mage.cards.decks.Deck; import mage.client.MageFrame; import mage.client.chat.ChatPanel; +import mage.client.constants.Constants.DeckEditorMode; import mage.client.plugins.impl.Plugins; import mage.client.util.GameManager; import mage.interfaces.callback.CallbackClient; @@ -44,6 +46,7 @@ import mage.view.AbilityPickerView; import mage.view.ChatMessage; import mage.view.GameClientMessage; import mage.view.GameView; +import mage.view.TableClientMessage; /** * @@ -73,9 +76,9 @@ public class Client implements CallbackClient { logger.info(callback.getMessageId() + " - " + callback.getMethod()); try { if (callback.getMethod().equals("startGame")) { - UUID[] data = (UUID[]) callback.getData(); - GameManager.getInstance().setCurrentPlayerUUID(data[1]); - gameStarted(data[0], data[1]); + TableClientMessage message = (TableClientMessage) callback.getData(); + GameManager.getInstance().setCurrentPlayerUUID(message.getPlayerId()); + gameStarted(message.getGameId(), message.getPlayerId()); } else if (callback.getMethod().equals("replayGame")) { replayGame(); @@ -154,6 +157,10 @@ public class Client implements CallbackClient { logger.warning("message out of sequence - ignoring"); } } + else if (callback.getMethod().equals("sideboard")) { + TableClientMessage message = (TableClientMessage) callback.getData(); + sideboard(message.getDeck(), message.getTableId()); + } messageId = callback.getMessageId(); } catch (Exception ex) { @@ -199,6 +206,10 @@ public class Client implements CallbackClient { } } + protected void sideboard(Deck deck, UUID tableId) { + frame.showDeckEditor(DeckEditorMode.Sideboard, deck, tableId); + } + private void handleException(Exception ex) { logger.log(Level.SEVERE, "Client error\n", ex); JOptionPane.showMessageDialog(MageFrame.getDesktop(), "Unrecoverable client error. Disconnecting", "Error", JOptionPane.ERROR_MESSAGE); diff --git a/Mage.Client/src/main/java/mage/client/remote/Session.java b/Mage.Client/src/main/java/mage/client/remote/Session.java index 10930130705..c3330228f85 100644 --- a/Mage.Client/src/main/java/mage/client/remote/Session.java +++ b/Mage.Client/src/main/java/mage/client/remote/Session.java @@ -40,8 +40,6 @@ import java.util.UUID; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JOptionPane; -import mage.Constants.MultiplayerAttackOption; -import mage.Constants.RangeOfInfluence; import mage.cards.decks.DeckCardLists; import mage.client.MageFrame; import mage.client.chat.ChatPanel; @@ -49,7 +47,6 @@ import mage.client.components.MageUI; import mage.client.game.GamePanel; import mage.client.util.Config; import mage.game.GameException; -import mage.game.match.MatchType; import mage.interfaces.MageException; import mage.game.match.MatchOptions; import mage.interfaces.Server; @@ -468,6 +465,20 @@ public class Session { return false; } + public boolean submitDeck(UUID tableId, DeckCardLists deck) { + try { + server.submitDeck(sessionId, tableId, deck); + return true; + } catch (RemoteException ex) { + handleRemoteException(ex); + } catch (MageException ex) { + handleMageException(ex); + } catch (GameException ex) { + handleGameException(ex); + } + return false; + } + public boolean concedeGame(UUID gameId) { try { server.concedeGame(gameId, sessionId); diff --git a/Mage.Common/src/mage/interfaces/Server.java b/Mage.Common/src/mage/interfaces/Server.java index 18ba540d470..fe80a748fdb 100644 --- a/Mage.Common/src/mage/interfaces/Server.java +++ b/Mage.Common/src/mage/interfaces/Server.java @@ -54,6 +54,7 @@ public interface Server extends Remote, CallbackServer { //table methods public TableView createTable(UUID sessionId, UUID roomId, MatchOptions matchOptions) throws RemoteException, MageException; public boolean joinTable(UUID sessionId, UUID roomId, UUID tableId, String name, DeckCardLists deckList) throws RemoteException, MageException, GameException; + public boolean submitDeck(UUID sessionId, UUID tableId, DeckCardLists deckList) throws RemoteException, MageException, GameException; public boolean watchTable(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException; public boolean replayTable(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException; public void leaveTable(UUID sessionId, UUID roomId, UUID tableId) throws RemoteException, MageException; diff --git a/Mage.Common/src/mage/view/TableClientMessage.java b/Mage.Common/src/mage/view/TableClientMessage.java new file mode 100644 index 00000000000..8bad71470b3 --- /dev/null +++ b/Mage.Common/src/mage/view/TableClientMessage.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.view; + +import java.io.Serializable; +import java.util.UUID; +import mage.cards.decks.Deck; + +/** + * + * @author BetaSteward_at_googlemail.com + */ +public class TableClientMessage implements Serializable { + + private Deck deck; + private UUID tableId; + private UUID gameId; + private UUID playerId; + + public TableClientMessage(Deck deck, UUID tableId) { + this.deck = deck; + this.tableId = tableId; + } + + public TableClientMessage(UUID gameId, UUID playerId) { + this.gameId = gameId; + this.playerId = playerId; + } + + public Deck getDeck() { + return deck; + } + + public UUID getTableId() { + return tableId; + } + + public UUID getGameId() { + return gameId; + } + + public UUID getPlayerId() { + return playerId; + } +} diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java index 7c53f6eab1b..09c82dba0ef 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/src/mage/game/TwoPlayerDuel.java @@ -63,8 +63,8 @@ public class TwoPlayerDuel extends GameImpl { } @Override - public void init() { - super.init(); + protected void init(UUID choosingPlayerId) { + super.init(choosingPlayerId); state.getTurnMods().add(new TurnMod(startingPlayerId, PhaseStep.DRAW)); } diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/mage/player/ai/ComputerPlayer.java index 5923c208414..8ade76a07b6 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/mage/player/ai/ComputerPlayer.java @@ -86,6 +86,7 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.common.FilterLandCard; import mage.filter.common.FilterNonlandCard; import mage.game.Game; +import mage.game.Table; import mage.game.combat.CombatGroup; import mage.game.permanent.Permanent; import mage.players.Player; @@ -111,7 +112,6 @@ import mage.util.TreeNode; public class ComputerPlayer> extends PlayerImpl implements Player { private final static transient Logger logger = Logging.getLogger(ComputerPlayer.class.getName()); - private boolean abort; private transient Map unplayable = new TreeMap(); private transient List playableNonInstant = new ArrayList(); private transient List playableInstant = new ArrayList(); @@ -128,7 +128,6 @@ public class ComputerPlayer> extends PlayerImpl i public ComputerPlayer(final ComputerPlayer player) { super(player); - this.abort = player.abort; } @Override @@ -768,6 +767,12 @@ public class ComputerPlayer> extends PlayerImpl i return super.getAvailableManaProducers(game); } + @Override + public void sideboard(Table table) { + //TODO: improve this + table.fireSubmitDeckEvent(playerId, deck); + } + protected Attackers getPotentialAttackers(Game game) { logger.fine("getAvailableAttackers"); Attackers attackers = new Attackers(); diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index c892ecfa986..70e2fadfb06 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -53,6 +53,7 @@ import mage.cards.decks.Deck; import mage.choices.ChoiceImpl; import mage.filter.common.FilterCreatureForCombat; import mage.game.Game; +import mage.game.Table; import mage.game.permanent.Permanent; import mage.target.Target; import mage.target.TargetAmount; @@ -71,8 +72,6 @@ public class HumanPlayer extends PlayerImpl { final transient PlayerResponse response = new PlayerResponse(); - private boolean abort; - protected static FilterCreatureForCombat filter = new FilterCreatureForCombat(); protected static Choice replacementEffectChoice = new ChoiceImpl(true); @@ -89,7 +88,6 @@ public class HumanPlayer extends PlayerImpl { public HumanPlayer(final HumanPlayer player) { super(player); - this.abort = player.abort; } protected void waitForResponse() { @@ -474,6 +472,11 @@ public class HumanPlayer extends PlayerImpl { return response.getInteger(); } + @Override + public void sideboard(Table table) { + table.fireSideboardEvent(playerId); + } + protected void specialAction(Game game) { Map specialActions = game.getState().getSpecialActions().getControlledBy(playerId); game.fireGetChoiceEvent(playerId, name, specialActions.values()); @@ -540,6 +543,8 @@ public class HumanPlayer extends PlayerImpl { } } + + @Override public HumanPlayer copy() { return new HumanPlayer(this); diff --git a/Mage.Server/plugins/mage-deck-constructed.jar b/Mage.Server/plugins/mage-deck-constructed.jar index 5ce9f4b46939206ea0c790644abc405b5a2e886e..022550af46c52ff6ea5b1682775e669b4a70dc62 100644 GIT binary patch delta 299 zcmcaBaZiFbz?+$civa{0cg&f{tHRs>q)etTgLtPEz|=cc5Vcv1aUKhZF?lU^>!ZX}^>I^O(3|yV2$5a08!MX>2vh%FB7}%s2WCX5mPK^(Kb1|mp zs+Nb={o|L^IiEQC zuG-&ef7|}&=S_0id7|sQ{J9!0rpr1pAG8=(x-UBTyV1t)H1o+)zB8J;@-2JMecQ$? zY$|{Mw^ds2x5I%4HcWeabYaLG?JcHX^Yks>y7mWzC;SWWW@Hj!hI_+yay_Rp$Rm@t ua@vEP`km7NOlxtOg6SkKH!!`L%L+`t;Bp4jdfa=EeDaw)l`W4G6gB`V`h0`{ diff --git a/Mage.Server/plugins/mage-deck-limited.jar b/Mage.Server/plugins/mage-deck-limited.jar index 57ea157b7a8aed85523de920e5a0d7a6634c18b3..b4ca904cc5da2bb8b5bc5ca4a5ce65769e1ba0e4 100644 GIT binary patch delta 203 zcmcaEbX|xyz?+$civa|hcFdW`tHRs}q)etTgLtPEz|=cc5Vcv1aVH~)G5Hdc7MT9e zWDJ%tWo}~<0!rBCJX#lA2sEh}h%Fc<2XN?2{>iE;tmzwC;OQG?uqnX6Xq%D2Mk9lu z$%bsfU^Cj;Oh7hH-p6JS;!k2X01F$ln}X?Vb|)~sf!zvBzh!rZ$a8GrfY~_tJx2xr DOsO4DB1ZJpn z`+*sgD|od)0+W~W8iOTn^Uh%61Pa&+ep<#;Jh?`|qu!CL*+78xLajo%L_u!H##W^r z8X-%$!@n{G|K)8`GwOB9kH7yZRhFqmSgq>+is#IZt8;ER^7lN+dvfYjoK|$uXQ>l6 z4`r1)>?`KkY8oD#@l^HQ?c!C7D>iRx57Wu~_hH7lH|I}S$4%VMv?cJ}=|xxN1H2iT zM3`Y-W1KVDNzer3xyhA+_F%{E7Bm3U?*vW3w3?7Bn9dXOg2)Sbf@yYPD=_UM>sO4DB1ZJpn z`+*sgD|od)0+W~W8iOTn^Uh%61Pa(19_xrKoLnQ|QNNL^*+78d!ruq~9v-+U(5%tLSEd}ZrWkYm{he05CuxGhDNnJg{~1rbnVbv@7wtatM5<`r+Sg`}4fme7 zc}gi}(Qzr~bl+qxmh~V1*iF}4Wo}*VnBe=m{rQyQGl6`2yl<}dkpH?Zm!m$wn~_O` z8SXLL$*zLNAg@iX7PJRDb+4cSnEoJW3Z^xLT)}jKkQYQ=$P-L+3R{6`H(_Tm-6OmU P$!Go|DQw1qpwI&V;%10K diff --git a/Mage.Server/plugins/mage-game-twoplayerduel.jar b/Mage.Server/plugins/mage-game-twoplayerduel.jar index 82888f63b1ab46ce292fc87bc305ef52569adc05..95284084f591bb36ea9b55ab173c760787b09c9b 100644 GIT binary patch delta 1848 zcmZ8ic{Cf?9wx*VA)y%+Yil{GwNz};R4s2PsoI-hsJ*d`mUe2-q*_%(5qs53?4?F5 zHLA9ZtxBz}eXEL{P$ObYYm&@+=bd@?oO^%Eckeyl`Tg;ID=JJCupOF@UxbIV;+qQ6 z!ODCO*g-dwk5lz4a>I@am|b0731wE>0(p4oBt>2X-r6b@8x7)xhg4MqYlT`%=r_pU zyH;s>3C|?V=}t9tzZpMvZ(mo~O;)J<+@Xj_{kI&RaHU&XYiMdN!PmS@&TC~0$%vd^ zZDH&e9xfl^k! z;vfV6j+g^$DJpXLqnm8tEw*kk(245;=!9XNJS%a%;DEMY7R&hk>C}=Ai3)~+#+bBS zn&jj~*8wAc%_=RKBh%iIF@xUl)BwrS(hL1>#r+Pf_D!R_U3;*10tVmOZic(Kx>{~E z6t)(v+Pr2Gy>5+M5b8p0q_p`emop|MccwbOSDNWn{ynKc9_dQB>LH*;Wl#v9t<(uo z+I#D1ARLhpo?5?7m&HUcx%*v5H&L@I zf|H-l4u@{`75Vs+ah9d}=KP%MLj34@Ot z5TTCU-HYiTC-Q2049+)iUDj9c6leKzI@M2p6FJOfC(MwrdN zSldfA`B5@Cits^T;RrW{N1;$iLnSJLRhOmc%cu^_oyO%cQsD##@z_F2$l+04<|U-@ z{m!eGV%;&XgRrwiolq?>q4QZoC3?rXKJE9PRlw)pZ%KufzO-T-%)@KqYT*M8u$l9X z{qpA@^%14-%l%p)iU@w4+$hNUqRWa8oK@d&S($cv0cGwP+&924-&A_I<(MkiWW9Im zN>@$B+K$U3R{EHc>~h5y(A{GB%Z6Q3KJucEq4xt$-92L}f!tIr8!X*E2oP+_{&w(W z$N2-$g_O7`obzhVJPW&d=OKc2q70Rvu+NdfVJ&e#!9E?UG)7It&H9a^QihawmA|4+ zNor-QEm-G4LYuUE?oXIejzc%U+%qAi=XaD>x1ebJPwBK)F;%=>oo|+B!4Xk zmSTao%YUoYHoepqKQ0NXp%jZD7EX?D4aH(|;&*5nVFfDro>rO{309)s)8Gxm!>Hk; zxC99c!J6@Ur%B0WQ?hv+87#VwEbvc^j{~Cm_Zvhb?T!&s;lBzj#8Ke-bb;#tj_TGg36*gZQZh)L z(;zj05L{pfL;(nM1ld4w@$_9%t{56`DiW^^;psRw`$k=yf{cAP*Hq>bKfPf8b}O-wdml}rxHtNkpz4clJ#9|SUOACK!=&<4M_d*g_0YZio@ z+$)E#iAx6-g|M^|x{aecGP^=>y6LBO7IxipU8hFmn;n`LaEk9g#sOfn^AXqOSO+Oz z{0f)tthhEeibM4N&pqrR#)=&JrfPrGNC0?v4)}O@&Tt**@1ul35+Lmf!AM;YIQNTB z5E;O-5WA&Fl81_NTN+RqULwf~D$lI~p)!DG5Q!oN7ykz@HXSM(+T}nxq!Xw*z*v-> n>T5-W1pn_MckX`=lT9F!y^a6DCR2?4#)LF10TJ*O=OBLp3ru2y delta 1825 zcmZ8iXHXN^8chO;5C{Ya5~>J`3q%8mp$BOa)=*WXNI-}oARUyt5Q%c;*&Qf^( zer^!ucqzjPDA75g7)UhWqe0P6xty~;#L7YnbGe!H2=p+$8GyB$( zYn08pXqy`i_%@B5n|-oVNnm7^t&uq&9%?GA;*c@#0rfWNXpu-4wgvUal##Ryp-_-E zvnQnM{HNV!W50KPJ71C$qluFZBO~b~1a2$z>77)>G-5e(q$*dXHj{CBo7B-G2FfyW zwHp+DDlny}*|d<#cd1>RYZ6=w39aU$JivVc7ct1Dy+|dcLGez}a9xX^$jpJ&LsaN+ zYM2u;FujkeHytyu(rbKok%3*IW9`+pI7|(mcdrx{3nw5*k)Ur<$d~PFMji0!$`XeH z3|V98W%jFEvulryEYjZAt=-AEM)Xg;>*8IAQP?QRB*_ocPFu?NXFj|bVrNLPIdAdO zE2yVHSKa%E{-LATUpjl0THB$J-8%^hLb1_aqNXV;7imy}n zH|Mq^^=#72CRLJ}h$8ODM)k<4&$nF~F{H@|!v0t`A4FD+x?|dVH+cm-fz!7F@+X?- zXySu_}lVXRFonAemZzhcNV;@Hs-)9Wk<;q z^>C>#>J>SCw#G2?D=@}g!4><$OJ?sV%Hn5*;Exs0uE`jjvXIv*OUtQjSy@KNf?NvJtBgf3_PP`QFL8sdm_0~btoXs zzN1L3r=|50C!f!j3BmVyN@*(DcCO}TUxqm}dn?{A$?qufbdo@ZppSfJqe3nuL--`9uX{QjfXAa*~TJR8H2qRyU&5+rnS?@uxvI$EdoLbWL>eWqrbx&(MbV zFqcV(P#l~o@2+z__LuVL0rXOF{_cvkTSqsDU$NxaT(#IWry^PYC1_34suw z5eMO~CRR#uDb65=4dAu!;R*lUBECnta zC*=ju%K7k=T4;Y(Q=c+hF(K_HGx6kLzke^-u8STkqjV5b zQ~nV9#Iq#f!k`n9Em13UVK82P>Ju%Y*!RlYXdn$|m;c zUimOQaCk4?xj4QQ8^W=B$`TI*Avpdv3Sbs3fd3N9Drgn_m)T^$@GG<9893(u+`<{I zE6rlBFMsr05Cj11asU8l*^cwqQ4;k4Lgf~wjl#v)FKoiaL1SE0ZfP1?fS29D3Wx(E zXm$cp>?%}19Q2HbR>LR8|8G`VwDuEdjs;z$@e3+~lz2~4HFU6-=RX(OL;q`%C*Oyh TY~6oxiKXQWT0w5`vzmVb<5ECJ diff --git a/Mage.Server/plugins/mage-player-ai.jar b/Mage.Server/plugins/mage-player-ai.jar index da0274094b85c257c15a88717a827d99caf1bdd6..ad99bdf71760f009018ac44dc9dbe5a173697d17 100644 GIT binary patch delta 16588 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 MEd!*N?SC!*AAiEGM*si- delta 16400 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 VXE5#3y9X)cIQvrBGTK2&1OUzq@<;#x delta 560 zcmex5hv~~4Cf)#VW)?065a`=Fb0V(_b1#rGnZgX>omK!-@6*Xci<37HJDMWCb&J zMSS1_Gu9?YKuqOG+r`8OGF3D$X{wMg14FC!WJfO1$?}cto8M$;34>Kj);wkbGyXTs z25Xxy3;q{8$mwvj=HP3c z_DDeEhWw6KrYt6!=~I4v|Ju90q9A4A8R32WC0mMFLV2cqJf?h$ZEf!BU0cQX@au0` zX0)Jen(2}ol6PNlO_{!>_V-HfM_--hIZk}?VWHvtm*+I*8_ViE-CC~L)>^mVUw}6w zlL#|3beVubSl(^|3g5}=+U-G(ocy8P08A@)n1kuq4l6J{tHTpaKkEnt(^j1jy06n8 z%zpson{+|=ZC!3)@$+3lU|OX+7))1nL*&muX}KOhuy}q?0GN*LkeqzGM-j~a-{TCX TU3>Q+1sqpjDqDIxD0u(?Z@ThN diff --git a/Mage.Server/plugins/mage-player-human.jar b/Mage.Server/plugins/mage-player-human.jar index 2f32a784d2ba60ce304bfa9bc69f0e64506ecfde..1cb91fb2ffb640263c6a19aeb4b83549e4240bdd 100644 GIT binary patch delta 8791 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 delta 8705 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 getTables(UUID roomId) throws MageException { try { diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index bd60d395159..055d334a20d 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -31,11 +31,13 @@ package mage.server; import java.util.logging.Level; import java.util.UUID; import java.util.logging.Logger; +import mage.cards.decks.Deck; import mage.interfaces.callback.CallbackServerSession; import mage.interfaces.callback.ClientCallback; import mage.server.game.GameManager; import mage.server.game.TableManager; import mage.util.Logging; +import mage.view.TableClientMessage; /** * @@ -90,7 +92,11 @@ public class Session { } public void gameStarted(final UUID gameId, final UUID playerId) { - fireCallback(new ClientCallback("startGame", new UUID[] {gameId, playerId})); + fireCallback(new ClientCallback("startGame", new TableClientMessage(gameId, playerId))); + } + + public void sideboard(final Deck deck, final UUID tableId) { + fireCallback(new ClientCallback("sideboard", new TableClientMessage(deck, tableId))); } public void watchGame(final UUID gameId) { diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index 8cb0b38f690..0092fd87563 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -82,15 +82,17 @@ public class GameController implements GameCallback { private Game game; private UUID chatId; private UUID tableId; + private UUID choosingPlayerId; private Future gameFuture; - public GameController(Game game, ConcurrentHashMap sessionPlayerMap, UUID tableId) { + public GameController(Game game, ConcurrentHashMap sessionPlayerMap, UUID tableId, UUID choosingPlayerId) { gameSessionId = UUID.randomUUID(); this.sessionPlayerMap = sessionPlayerMap; chatId = ChatManager.getInstance().createChatSession(); this.game = game; this.tableId = tableId; + this.choosingPlayerId = choosingPlayerId; init(); } @@ -186,7 +188,7 @@ public class GameController implements GameCallback { return; } } - GameWorker worker = new GameWorker(game, this); + GameWorker worker = new GameWorker(game, choosingPlayerId, this); gameFuture = gameExecutor.submit(worker); } } diff --git a/Mage.Server/src/main/java/mage/server/game/GameManager.java b/Mage.Server/src/main/java/mage/server/game/GameManager.java index 14148324d28..dd44bf5eaf8 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameManager.java +++ b/Mage.Server/src/main/java/mage/server/game/GameManager.java @@ -49,8 +49,8 @@ public class GameManager { private ConcurrentHashMap gameControllers = new ConcurrentHashMap(); - public UUID createGameSession(Game game, ConcurrentHashMap sessionPlayerMap, UUID tableId) { - GameController gameController = new GameController(game, sessionPlayerMap, tableId); + public UUID createGameSession(Game game, ConcurrentHashMap sessionPlayerMap, UUID tableId, UUID choosingPlayerId) { + GameController gameController = new GameController(game, sessionPlayerMap, tableId, choosingPlayerId); gameControllers.put(game.getId(), gameController); return gameController.getSessionId(); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameWorker.java b/Mage.Server/src/main/java/mage/server/game/GameWorker.java index 7566ab1dc1b..c0c15037f58 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameWorker.java +++ b/Mage.Server/src/main/java/mage/server/game/GameWorker.java @@ -28,6 +28,7 @@ package mage.server.game; +import java.util.UUID; import java.util.concurrent.Callable; import java.util.logging.Level; import java.util.logging.Logger; @@ -44,16 +45,18 @@ public class GameWorker implements Callable { private GameCallback result; private Game game; + private UUID choosingPlayerId; - public GameWorker(Game game, GameCallback result) { + public GameWorker(Game game, UUID choosingPlayerId, GameCallback result) { this.game = game; + this.choosingPlayerId = choosingPlayerId; this.result = result; } @Override public Object call() { try { - game.start(); + game.start(choosingPlayerId); result.gameResult(game.getWinner()); } catch (Exception ex) { logger.log(Level.SEVERE, null, ex); diff --git a/Mage.Server/src/main/java/mage/server/game/GamesRoom.java b/Mage.Server/src/main/java/mage/server/game/GamesRoom.java index 901a6f12fb9..39901f5103b 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoom.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoom.java @@ -30,11 +30,8 @@ package mage.server.game; import java.util.List; import java.util.UUID; -import mage.Constants.MultiplayerAttackOption; -import mage.Constants.RangeOfInfluence; import mage.cards.decks.DeckCardLists; import mage.game.GameException; -import mage.game.match.MatchType; import mage.game.match.MatchOptions; import mage.view.TableView; diff --git a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java index f327c233065..51d29d8e26a 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java @@ -35,11 +35,8 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; -import mage.Constants.MultiplayerAttackOption; -import mage.Constants.RangeOfInfluence; import mage.cards.decks.DeckCardLists; import mage.game.GameException; -import mage.game.match.MatchType; import mage.game.match.MatchOptions; import mage.util.Logging; import mage.view.TableView; diff --git a/Mage.Server/src/main/java/mage/server/game/TableController.java b/Mage.Server/src/main/java/mage/server/game/TableController.java index 48da85e1ee2..579b59c2e67 100644 --- a/Mage.Server/src/main/java/mage/server/game/TableController.java +++ b/Mage.Server/src/main/java/mage/server/game/TableController.java @@ -39,7 +39,6 @@ import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.OutputStream; -import java.util.List; import java.util.Map.Entry; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -47,8 +46,6 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -import mage.Constants.MultiplayerAttackOption; -import mage.Constants.RangeOfInfluence; import mage.Constants.TableState; import mage.cards.decks.Deck; import mage.cards.decks.DeckCardLists; @@ -56,9 +53,11 @@ import mage.game.Game; import mage.game.GameException; import mage.game.GameStates; import mage.game.match.Match; -import mage.game.match.MatchType; import mage.game.Seat; +import mage.game.events.Listener; +import mage.game.events.TableEvent; import mage.game.match.MatchOptions; +import mage.game.match.MatchPlayer; import mage.players.Player; import mage.server.ChatManager; import mage.server.Main; @@ -76,7 +75,6 @@ public class TableController { private UUID sessionId; private UUID chatId; - //private UUID gameId; private Table table; private Match match; private MatchOptions options; @@ -87,8 +85,26 @@ public class TableController { chatId = ChatManager.getInstance().createChatSession(); this.options = options; match = GameFactory.getInstance().createMatch(options.getGameType(), options); - //gameId = game.getId(); - table = new Table(options.getGameType(), DeckValidatorFactory.getInstance().createDeckValidator(options.getDeckType()), options.getPlayerTypes()); + table = new Table(options.getGameType(), options.getName(), DeckValidatorFactory.getInstance().createDeckValidator(options.getDeckType()), options.getPlayerTypes()); + init(); + } + + private void init() { + table.addTableEventListener( + new Listener () { + @Override + public void event(TableEvent event) { + switch (event.getEventType()) { + case SIDEBOARD: + sideboard(event.getPlayerId()); + break; + case SUBMIT_DECK: + submitDeck(event.getPlayerId(), event.getDeck()); + break; + } + } + } + ); } public synchronized boolean joinTable(UUID sessionId, String name, DeckCardLists deckList) throws GameException { @@ -116,6 +132,24 @@ public class TableController { return true; } + public synchronized boolean submitDeck(UUID sessionId, DeckCardLists deckList) throws GameException { + if (table.getState() != TableState.SIDEBOARDING) { + return false; + } + MatchPlayer player = match.getPlayer(sessionPlayerMap.get(sessionId)); + Deck deck = Deck.load(deckList); + if (!Main.server.isTestMode() && !validDeck(deck)) { + throw new GameException(player.getPlayer().getName() + " has an invalid deck for this format"); + } + submitDeck(sessionPlayerMap.get(sessionId), deck); + return true; + } + + private void submitDeck(UUID playerId, Deck deck) { + MatchPlayer player = match.getPlayer(playerId); + player.submitDeck(deck); + } + public boolean watchTable(UUID sessionId) { if (table.getState() != TableState.DUELING) { return false; @@ -159,30 +193,53 @@ public class TableController { if (sessionId.equals(this.sessionId) && table.getState() == TableState.STARTING) { try { match.startMatch(); - startGame(); + startGame(null); } catch (GameException ex) { logger.log(Level.SEVERE, null, ex); } } } - private void startGame() throws GameException { + private void startGame(UUID choosingPlayerId) throws GameException { match.startGame(); - GameManager.getInstance().createGameSession(match.getGame(), sessionPlayerMap, table.getId()); + table.initGame(); + GameManager.getInstance().createGameSession(match.getGame(), sessionPlayerMap, table.getId(), choosingPlayerId); SessionManager sessionManager = SessionManager.getInstance(); for (Entry entry: sessionPlayerMap.entrySet()) { sessionManager.getSession(entry.getKey()).gameStarted(match.getGame().getId(), entry.getValue()); } } + private void sideboard() { + table.sideboard(); + for (MatchPlayer player: match.getPlayers()) { + player.setSideboarding(); + player.getPlayer().sideboard(table); + } + while (!match.isDoneSideboarding()){} + } + + private void sideboard(UUID playerId) { + SessionManager sessionManager = SessionManager.getInstance(); + for (Entry entry: sessionPlayerMap.entrySet()) { + if (entry.getValue().equals(playerId)) { + MatchPlayer player = match.getPlayer(entry.getValue()); + sessionManager.getSession(entry.getKey()).sideboard(player.getDeck(), table.getId()); + break; + } + } + } + public void endGame() { + UUID choosingPlayerId = match.getChooser(); match.endGame(); table.endGame(); saveGame(); GameManager.getInstance().removeGame(match.getGame().getId()); try { if (!match.isMatchOver()) { - startGame(); + sideboard(); + startGame(choosingPlayerId); } } catch (GameException ex) { logger.log(Level.SEVERE, null, ex); diff --git a/Mage.Server/src/main/java/mage/server/game/TableManager.java b/Mage.Server/src/main/java/mage/server/game/TableManager.java index d0ffc29cc41..2262a96704f 100644 --- a/Mage.Server/src/main/java/mage/server/game/TableManager.java +++ b/Mage.Server/src/main/java/mage/server/game/TableManager.java @@ -30,15 +30,11 @@ package mage.server.game; import mage.game.Table; import java.util.Collection; -import java.util.List; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; -import mage.Constants.MultiplayerAttackOption; -import mage.Constants.RangeOfInfluence; import mage.cards.decks.DeckCardLists; import mage.game.GameException; -import mage.game.match.MatchType; import mage.game.match.MatchOptions; import mage.util.Logging; @@ -77,6 +73,10 @@ public class TableManager { return controllers.get(tableId).joinTable(sessionId, name, deckList); } + public boolean submitDeck(UUID sessionId, UUID tableId, DeckCardLists deckList) throws GameException { + return controllers.get(tableId).submitDeck(sessionId, deckList); + } + public void removeSession(UUID sessionId) { // TODO: search through tables and remove session } diff --git a/Mage/src/mage/game/Game.java b/Mage/src/mage/game/Game.java index 0bf3bc24f8f..71fadddf838 100644 --- a/Mage/src/mage/game/Game.java +++ b/Mage/src/mage/game/Game.java @@ -131,8 +131,8 @@ public interface Game extends MageItem, Serializable { public boolean replaceEvent(GameEvent event); //game play methods - public void init(); - public void start(); +// public void init(UUID choosingPlayerId); + public void start(UUID choosingPlayerId); public void end(); public void mulligan(UUID playerId); public void quit(UUID playerId); diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 40b4ad16f05..8cad1b1a18a 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -109,8 +109,9 @@ public abstract class GameImpl> implements Game, Serializa protected Map gameCards = new HashMap(); protected GameState state; protected UUID startingPlayerId; - protected UUID choosingPlayerId; +// protected UUID choosingPlayerId; protected UUID winnerId; + protected transient GameStates gameStates = new GameStates(); protected RangeOfInfluence range; protected MultiplayerAttackOption attackOption; @@ -129,7 +130,7 @@ public abstract class GameImpl> implements Game, Serializa this.id = game.id; this.ready = game.ready; this.startingPlayerId = game.startingPlayerId; - this.choosingPlayerId = game.choosingPlayerId; +// this.choosingPlayerId = game.choosingPlayerId; this.winnerId = game.winnerId; this.range = game.range; this.attackOption = game.attackOption; @@ -299,8 +300,8 @@ public abstract class GameImpl> implements Game, Serializa } @Override - public void start() { - init(); + public void start(UUID choosingPlayerId) { + init(choosingPlayerId); PlayerList players = state.getPlayerList(startingPlayerId); Player player = getPlayer(players.get()); while (!isGameOver()) { @@ -321,8 +322,7 @@ public abstract class GameImpl> implements Game, Serializa saveState(); } - @Override - public void init() { + protected void init(UUID choosingPlayerId) { for (Player player: state.getPlayers().values()) { player.init(this); } @@ -335,19 +335,24 @@ public abstract class GameImpl> implements Game, Serializa } //20091005 - 103.2 - if (startingPlayerId == null) { - TargetPlayer targetPlayer = new TargetPlayer(); - targetPlayer.setRequired(true); - targetPlayer.setTargetName("starting player"); - Player choosingPlayer = getPlayer(pickChoosingPlayer()); - if (choosingPlayer.chooseTarget(Outcome.Benefit, targetPlayer, null, this)) { - startingPlayerId = ((List)targetPlayer.getTargets()).get(0); - fireInformEvent(state.getPlayer(startingPlayerId).getName() + " will start"); - } - else { - return; - } + TargetPlayer targetPlayer = new TargetPlayer(); + targetPlayer.setRequired(true); + targetPlayer.setTargetName("starting player"); + Player choosingPlayer; + if (choosingPlayerId == null) { + choosingPlayer = getPlayer(pickChoosingPlayer()); } + else { + choosingPlayer = this.getPlayer(choosingPlayerId); + } + if (choosingPlayer.chooseTarget(Outcome.Benefit, targetPlayer, null, this)) { + startingPlayerId = ((List)targetPlayer.getTargets()).get(0); + fireInformEvent(state.getPlayer(startingPlayerId).getName() + " will start"); + } + else { + return; + } + saveState(); //20091005 - 103.3 diff --git a/Mage/src/mage/game/Table.java b/Mage/src/mage/game/Table.java index 464bb5f355e..0cfe414008d 100644 --- a/Mage/src/mage/game/Table.java +++ b/Mage/src/mage/game/Table.java @@ -32,7 +32,12 @@ import java.io.Serializable; import java.util.List; import java.util.UUID; import mage.Constants.TableState; +import mage.cards.decks.Deck; import mage.cards.decks.DeckValidator; +import mage.game.events.Listener; +import mage.game.events.TableEvent; +import mage.game.events.TableEvent.EventType; +import mage.game.events.TableEventSource; import mage.players.Player; /** @@ -49,10 +54,13 @@ public class Table implements Serializable { private DeckValidator validator; private TableState state = TableState.WAITING; - public Table(String gameType, DeckValidator validator, List playerTypes) { + protected TableEventSource tableEventSource = new TableEventSource(); + + public Table(String gameType, String name, DeckValidator validator, List playerTypes) { tableId = UUID.randomUUID(); this.numSeats = playerTypes.size(); this.gameType = gameType; + this.name = name; createSeats(playerTypes); this.validator = validator; } @@ -70,10 +78,7 @@ public class Table implements Serializable { return tableId; } - public void initGame(Game game) throws GameException { - for (int i = 0; i < numSeats; i++ ) { - game.addPlayer(seats[i].getPlayer()); - } + public void initGame() { state = TableState.DUELING; } @@ -134,4 +139,23 @@ public class Table implements Serializable { return this.validator; } + public void sideboard() { + state = TableState.SIDEBOARDING; + } + + public String getName() { + return this.name; + } + + public void fireSideboardEvent(UUID playerId) { + tableEventSource.fireTableEvent(EventType.SIDEBOARD, playerId, null); + } + + public void fireSubmitDeckEvent(UUID playerId, Deck deck) { + tableEventSource.fireTableEvent(EventType.SUBMIT_DECK, playerId, deck); + } + + public void addTableEventListener(Listener listener) { + tableEventSource.addListener(listener); + } } diff --git a/Mage/src/mage/game/events/TableEvent.java b/Mage/src/mage/game/events/TableEvent.java index c9b915c4aa6..a7f52d8d650 100644 --- a/Mage/src/mage/game/events/TableEvent.java +++ b/Mage/src/mage/game/events/TableEvent.java @@ -30,7 +30,9 @@ package mage.game.events; import java.io.Serializable; import java.util.EventObject; +import java.util.UUID; import mage.cards.Cards; +import mage.cards.decks.Deck; import mage.game.Game; /** @@ -40,13 +42,15 @@ import mage.game.Game; public class TableEvent extends EventObject implements ExternalEvent, Serializable { public enum EventType { - UPDATE, INFO, REVEAL, LOOK + UPDATE, INFO, REVEAL, LOOK, SIDEBOARD, SUBMIT_DECK } private Game game; private EventType eventType; private String message; private Cards cards; + private UUID playerId; + private Deck deck; public TableEvent(EventType eventType, String message, Cards cards, Game game) { super(game); @@ -56,6 +60,13 @@ public class TableEvent extends EventObject implements ExternalEvent, Serializab this.eventType = eventType; } + public TableEvent(EventType eventType, UUID playerId, Deck deck) { + super(playerId); + this.playerId = playerId; + this.deck = deck; + this.eventType = eventType; + } + public Game getGame() { return game; } @@ -72,4 +83,11 @@ public class TableEvent extends EventObject implements ExternalEvent, Serializab return cards; } + public UUID getPlayerId() { + return playerId; + } + + public Deck getDeck() { + return deck; + } } diff --git a/Mage/src/mage/game/events/TableEventSource.java b/Mage/src/mage/game/events/TableEventSource.java index 410df526ca6..4b46273a35d 100644 --- a/Mage/src/mage/game/events/TableEventSource.java +++ b/Mage/src/mage/game/events/TableEventSource.java @@ -29,8 +29,11 @@ package mage.game.events; import java.io.Serializable; +import java.util.UUID; import mage.cards.Cards; +import mage.cards.decks.Deck; import mage.game.Game; +import mage.game.events.TableEvent.EventType; /** * @@ -45,11 +48,15 @@ public class TableEventSource implements EventSource, Serializable { dispatcher.addListener(listener); } - public void fireTableEvent(TableEvent.EventType eventType, String message, Game game) { + public void fireTableEvent(EventType eventType, String message, Game game) { dispatcher.fireEvent(new TableEvent(eventType, message, null, game)); } - public void fireTableEvent(TableEvent.EventType eventType, String message, Cards cards, Game game) { + public void fireTableEvent(EventType eventType, String message, Cards cards, Game game) { dispatcher.fireEvent(new TableEvent(eventType, message, cards, game)); } + + public void fireTableEvent(EventType eventType, UUID playerId, Deck deck) { + dispatcher.fireEvent(new TableEvent(eventType, playerId, deck)); + } } diff --git a/Mage/src/mage/game/match/Match.java b/Mage/src/mage/game/match/Match.java index 0c938da2236..7364ab57637 100644 --- a/Mage/src/mage/game/match/Match.java +++ b/Mage/src/mage/game/match/Match.java @@ -44,10 +44,14 @@ public interface Match { public UUID getId(); public boolean isMatchOver(); public List getPlayers(); + public MatchPlayer getPlayer(UUID playerId); public void addPlayer(Player player, Deck deck); public void startMatch() throws GameException; public void startGame() throws GameException; public void endGame(); public Game getGame(); - + public int getNumGames(); + public boolean isDoneSideboarding(); + public UUID getChooser(); + } diff --git a/Mage/src/mage/game/match/MatchImpl.java b/Mage/src/mage/game/match/MatchImpl.java index 0dfa4e1d6de..2ce1488afb7 100644 --- a/Mage/src/mage/game/match/MatchImpl.java +++ b/Mage/src/mage/game/match/MatchImpl.java @@ -56,6 +56,15 @@ public abstract class MatchImpl implements Match { return players; } + @Override + public MatchPlayer getPlayer(UUID playerId) { + for (MatchPlayer player: players) { + if (player.getPlayer().getId().equals(playerId)) + return player; + } + return null; + } + @Override public void addPlayer(Player player, Deck deck) { MatchPlayer mPlayer = new MatchPlayer(player, deck); @@ -87,6 +96,11 @@ public abstract class MatchImpl implements Match { return games.get(games.size() -1); } + @Override + public int getNumGames() { + return games.size(); + } + protected void initGame(Game game) throws GameException { for (MatchPlayer matchPlayer: this.players) { game.addPlayer(matchPlayer.getPlayer()); @@ -108,4 +122,28 @@ public abstract class MatchImpl implements Match { } } } + + @Override + public UUID getChooser() { + UUID loserId = null; + Game game = getGame(); + for (MatchPlayer player: this.players) { + Player p = game.getPlayer(player.getPlayer().getId()); + if (p != null) { + if (p.hasLost()) + loserId = p.getId(); + } + } + return loserId; + } + + @Override + public boolean isDoneSideboarding() { + for (MatchPlayer player: this.players) { + if (!player.isDoneSideboarding()) + return false; + } + return true; + } + } diff --git a/Mage/src/mage/game/match/MatchPlayer.java b/Mage/src/mage/game/match/MatchPlayer.java index cf013ded155..6408ad00758 100644 --- a/Mage/src/mage/game/match/MatchPlayer.java +++ b/Mage/src/mage/game/match/MatchPlayer.java @@ -40,12 +40,14 @@ public class MatchPlayer { private int loses; private Deck deck; private Player player; + private boolean doneSideboarding; public MatchPlayer(Player player, Deck deck) { this.player = player; this.deck = deck; this.wins = 0; this.loses = 0; + this.doneSideboarding = true; } public int getWins() { @@ -68,8 +70,21 @@ public class MatchPlayer { return deck; } + public void submitDeck(Deck deck) { + this.deck = deck; + this.doneSideboarding = true; + } + public Player getPlayer() { return player; } + public void setSideboarding() { + this.doneSideboarding = false; + } + + public boolean isDoneSideboarding() { + return this.doneSideboarding; + } + } diff --git a/Mage/src/mage/players/Player.java b/Mage/src/mage/players/Player.java index 74f434d3f49..16bc4ecdf35 100644 --- a/Mage/src/mage/players/Player.java +++ b/Mage/src/mage/players/Player.java @@ -46,11 +46,13 @@ import mage.abilities.effects.ReplacementEffect; import mage.abilities.mana.ManaOptions; import mage.cards.Card; import mage.cards.Cards; +import mage.cards.decks.Deck; import mage.choices.Choice; import mage.counters.Counters; import mage.filter.FilterAbility; import mage.game.events.GameEvent; import mage.game.Game; +import mage.game.Table; import mage.game.permanent.Permanent; import mage.target.Target; import mage.target.TargetAmount; @@ -144,6 +146,7 @@ public interface Player extends MageItem, Copyable { public abstract void selectBlockers(Game game); public abstract void assignDamage(int damage, List targets, UUID sourceId, Game game); public abstract int getAmount(int min, int max, String message, Game game); + public abstract void sideboard(Table table); public void declareAttacker(UUID attackerId, UUID defenderId, Game game); public void declareBlocker(UUID blockerId, UUID attackerId, Game game); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 40dd4e3f137..e6b58264df9 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -79,6 +79,7 @@ import mage.target.common.TargetDiscard; public abstract class PlayerImpl> implements Player, Serializable { + protected boolean abort; protected final UUID playerId; protected String name; protected boolean human; @@ -122,6 +123,7 @@ public abstract class PlayerImpl> implements Player, Ser } public PlayerImpl(final PlayerImpl player) { + this.abort = player.abort; this.playerId = player.playerId; this.name = player.name; this.human = player.human; @@ -148,6 +150,7 @@ public abstract class PlayerImpl> implements Player, Ser @Override public void init(Game game) { + this.abort = false; library.addAll(deck.getCards(), game); this.hand.clear(); this.graveyard.clear(); From 6fcb471f7f0e191e22af6bac7a293b17abca0953 Mon Sep 17 00:00:00 2001 From: Loki Date: Wed, 29 Dec 2010 08:03:44 +0200 Subject: [PATCH 13/23] SOM --- .../sets/scarsofmirrodin/AcidWebSpider.java | 80 ++++++++++++++ .../sets/scarsofmirrodin/ArgentumArmor.java | 103 ++++++++++++++++++ .../scarsofmirrodin/BarbedBattlegear.java | 66 +++++++++++ .../sets/scarsofmirrodin/BladedPinions.java | 69 ++++++++++++ .../scarsofmirrodin/VulshokHeartstoker.java | 71 ++++++++++++ 5 files changed, 389 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/AcidWebSpider.java create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentumArmor.java create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/BarbedBattlegear.java create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/BladedPinions.java create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/VulshokHeartstoker.java diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/AcidWebSpider.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/AcidWebSpider.java new file mode 100644 index 00000000000..967d4ad0ae9 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/AcidWebSpider.java @@ -0,0 +1,80 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.scarsofmirrodin; + +import java.util.UUID; +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.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.ReachAbility; +import mage.cards.CardImpl; +import mage.filter.Filter; +import mage.filter.FilterPermanent; +import mage.target.TargetPermanent; + +/** + * + * @author Loki + */ +public class AcidWebSpider extends CardImpl { + private static FilterPermanent filter = new FilterPermanent("Equipment"); + + static { + filter.getSubtype().add("Equipment"); + filter.setScopeSubtype(Filter.ComparisonScope.Any); + } + + public AcidWebSpider (UUID ownerId) { + super(ownerId, 108, "Acid Web Spider", Rarity.UNCOMMON, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Spider"); + this.color.setGreen(true); + this.power = new MageInt(3); + this.toughness = new MageInt(5); + this.addAbility(ReachAbility.getInstance()); + Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), true); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(ability); + } + + public AcidWebSpider (final AcidWebSpider card) { + super(card); + } + + @Override + public AcidWebSpider copy() { + return new AcidWebSpider(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentumArmor.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentumArmor.java new file mode 100644 index 00000000000..83580aaa738 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/ArgentumArmor.java @@ -0,0 +1,103 @@ +/* + * 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.scarsofmirrodin; + +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.abilities.TriggeredAbilityImpl; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.BoostEquippedEffect; +import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.permanent.Permanent; +import mage.target.TargetPermanent; + +/** + * + * @author Loki + */ +public class ArgentumArmor extends CardImpl { + + public ArgentumArmor (UUID ownerId) { + super(ownerId, 137, "Argentum Armor", Rarity.RARE, new CardType[]{CardType.ARTIFACT}, "{6}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Equipment"); + this.addAbility(new EquipAbility(Constants.Outcome.AddAbility, new GenericManaCost(6))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(6, 6))); + this.addAbility(new ArgentumArmorAbiltity()); + } + + public ArgentumArmor (final ArgentumArmor card) { + super(card); + } + + @Override + public ArgentumArmor copy() { + return new ArgentumArmor(this); + } +} + +class ArgentumArmorAbiltity extends TriggeredAbilityImpl { + public ArgentumArmorAbiltity() { + super(Zone.BATTLEFIELD, new DestroyTargetEffect()); + this.addTarget(new TargetPermanent()); + } + + public ArgentumArmorAbiltity(final ArgentumArmorAbiltity abiltity) { + super(abiltity); + } + + @Override + public ArgentumArmorAbiltity copy() { + return new ArgentumArmorAbiltity(this); + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + Permanent equipment = game.getPermanent(this.sourceId); + if (equipment != null && equipment.getAttachedTo() != null && event.getType() == GameEvent.EventType.ATTACKER_DECLARED && event.getSourceId().equals(equipment.getAttachedTo())) { + return true; + } + return false; + } + + @Override + public String getRule() { + return "Whenever equipped creature attacks, destroy target permanent."; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/BarbedBattlegear.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/BarbedBattlegear.java new file mode 100644 index 00000000000..3baf2f5ef71 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/BarbedBattlegear.java @@ -0,0 +1,66 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.scarsofmirrodin; + +import java.util.UUID; + +import mage.Constants; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.BoostEquippedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.cards.CardImpl; + +/** + * + * @author Loki + */ +public class BarbedBattlegear extends CardImpl { + + public BarbedBattlegear (UUID ownerId) { + super(ownerId, 139, "Barbed Battlegear", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{3}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Equipment"); + this.addAbility(new EquipAbility(Constants.Outcome.AddAbility, new GenericManaCost(2))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostEquippedEffect(4, -1))); + } + + public BarbedBattlegear (final BarbedBattlegear card) { + super(card); + } + + @Override + public BarbedBattlegear copy() { + return new BarbedBattlegear(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/BladedPinions.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/BladedPinions.java new file mode 100644 index 00000000000..df6637a8f78 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/BladedPinions.java @@ -0,0 +1,69 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.scarsofmirrodin; + +import java.util.UUID; + +import mage.Constants; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.costs.mana.GenericManaCost; +import mage.abilities.effects.common.GainAbilityAttachedEffect; +import mage.abilities.keyword.EquipAbility; +import mage.abilities.keyword.FirstStrikeAbility; +import mage.abilities.keyword.FlyingAbility; +import mage.cards.CardImpl; + +/** + * + * @author Loki + */ +public class BladedPinions extends CardImpl { + + public BladedPinions (UUID ownerId) { + super(ownerId, 140, "Bladed Pinions", Rarity.COMMON, new CardType[]{CardType.ARTIFACT}, "{2}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Equipment"); + this.addAbility(new EquipAbility(Constants.Outcome.AddAbility, new GenericManaCost(2))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FlyingAbility.getInstance()))); + this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance()))); + } + + public BladedPinions (final BladedPinions card) { + super(card); + } + + @Override + public BladedPinions copy() { + return new BladedPinions(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/VulshokHeartstoker.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/VulshokHeartstoker.java new file mode 100644 index 00000000000..009f3d08209 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/VulshokHeartstoker.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.scarsofmirrodin; + +import java.util.UUID; +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.EntersBattlefieldTriggeredAbility; +import mage.abilities.effects.common.BoostTargetEffect; +import mage.cards.CardImpl; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Loki + */ +public class VulshokHeartstoker extends CardImpl { + + public VulshokHeartstoker (UUID ownerId) { + super(ownerId, 107, "Vulshok Heartstoker", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{R}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Human"); + this.subtype.add("Shaman"); + this.color.setRed(true); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + Ability ability = new EntersBattlefieldTriggeredAbility(new BoostTargetEffect(2, 0, Duration.EndOfTurn)); + ability.addTarget(new TargetCreaturePermanent()); + this.addAbility(ability); + } + + public VulshokHeartstoker (final VulshokHeartstoker card) { + super(card); + } + + @Override + public VulshokHeartstoker copy() { + return new VulshokHeartstoker(this); + } + +} From c54ce8eb22f6659314c1f9210a9a814657b75c69 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Wed, 29 Dec 2010 12:26:00 +0300 Subject: [PATCH 14/23] [mage.core] Reimplemented Counters: using enums instead of strings. Now supports only P1P1, M1M1, POISON. Feel free to add more. --- Mage/src/mage/counters/CounterType.java | 81 +++++++++++++++++++++++ Mage/src/mage/counters/Counters.java | 6 ++ Mage/src/mage/counters/PoisonCounter.java | 47 +++++++++++++ Mage/src/mage/game/GameImpl.java | 3 +- 4 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 Mage/src/mage/counters/CounterType.java create mode 100644 Mage/src/mage/counters/PoisonCounter.java diff --git a/Mage/src/mage/counters/CounterType.java b/Mage/src/mage/counters/CounterType.java new file mode 100644 index 00000000000..6447dcc172a --- /dev/null +++ b/Mage/src/mage/counters/CounterType.java @@ -0,0 +1,81 @@ +/* +* 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.counters; + +/** + * Enum for counters, names and instances. + * + * @author nantuko + */ +public enum CounterType { + P1P1(new PlusOneCounter().name), + M1M1(new MinusOneCounter().name), + POISON(new PoisonCounter().name); + + private String name; + + private CounterType(String name) { + this.name = name; + } + + /** + * Get counter string name. + * + * @return + */ + public String getName() { + return this.name; + } + + /** + * Get instance of counter type with amount equal to 1. + * + * @return + */ + public Counter getInstance() { + return getInstance(1); + } + + /** + * Get instance of counter type with defined amount of the given type. + * + * @param amount amount of counters of the given type. + * @return + */ + public Counter getInstance(int amount) { + switch(this) { + case P1P1: + return new PlusOneCounter(amount); + case M1M1: + return new MinusOneCounter(amount); + case POISON: + return new PoisonCounter(amount); + } + } +} diff --git a/Mage/src/mage/counters/Counters.java b/Mage/src/mage/counters/Counters.java index 625db5a855a..3b142957427 100644 --- a/Mage/src/mage/counters/Counters.java +++ b/Mage/src/mage/counters/Counters.java @@ -86,6 +86,12 @@ public class Counters extends HashMap implements Serializable { return 0; } + public int getCount(CounterType type) { + if (this.containsKey(type.getName())) + return this.get(type.getName()).getCount(); + return 0; + } + public List getBoostCounters() { List boosters = new ArrayList(); for (Counter counter: this.values()) { diff --git a/Mage/src/mage/counters/PoisonCounter.java b/Mage/src/mage/counters/PoisonCounter.java new file mode 100644 index 00000000000..bbcf8dab39b --- /dev/null +++ b/Mage/src/mage/counters/PoisonCounter.java @@ -0,0 +1,47 @@ +/* +* 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.counters; + +/** + * Poison counter. + * + * @author nantuko + */ +public class PoisonCounter extends Counter { + + public PoisonCounter() { + super("Poison"); + this.count = 1; + } + + public PoisonCounter(int amount) { + super("Poison"); + this.count = amount; + } +} diff --git a/Mage/src/mage/game/GameImpl.java b/Mage/src/mage/game/GameImpl.java index 8cad1b1a18a..f34f000181f 100644 --- a/Mage/src/mage/game/GameImpl.java +++ b/Mage/src/mage/game/GameImpl.java @@ -28,6 +28,7 @@ package mage.game; +import mage.counters.CounterType; import mage.game.match.MatchType; import java.io.IOException; import mage.game.stack.SpellStack; @@ -582,7 +583,7 @@ public abstract class GameImpl> implements Game, Serializa //20091005 - 704.5a/704.5b/704.5c for (Player player: state.getPlayers().values()) { - if (!player.hasLost() && (player.getLife() <= 0 || player.isEmptyDraw() || player.getCounters().getCount("Poison") >= 10)) { + if (!player.hasLost() && (player.getLife() <= 0 || player.isEmptyDraw() || player.getCounters().getCount(CounterType.POISON) >= 10)) { player.lost(this); } } From a5e6a3109b21263f66bc0fbbb8802d4ad77bbe3d Mon Sep 17 00:00:00 2001 From: magenoxx Date: Wed, 29 Dec 2010 12:32:32 +0300 Subject: [PATCH 15/23] [mage.core] Infect and Wither abilities. --- .../mage/abilities/keyword/InfectAbility.java | 84 +++++++++++++++++++ .../mage/abilities/keyword/WitherAbility.java | 75 +++++++++++++++++ .../mage/game/permanent/PermanentImpl.java | 17 ++-- Mage/src/mage/players/PlayerImpl.java | 13 +-- 4 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 Mage/src/mage/abilities/keyword/InfectAbility.java create mode 100644 Mage/src/mage/abilities/keyword/WitherAbility.java diff --git a/Mage/src/mage/abilities/keyword/InfectAbility.java b/Mage/src/mage/abilities/keyword/InfectAbility.java new file mode 100644 index 00000000000..5c35d540a2d --- /dev/null +++ b/Mage/src/mage/abilities/keyword/InfectAbility.java @@ -0,0 +1,84 @@ +/* +* 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.keyword; + +import java.io.ObjectStreamException; +import mage.Constants.Zone; +import mage.abilities.StaticAbility; + +/** + * 702.87. Infect + * + * 702.87a. Infect is a static ability. + * + * 702.87b. Damage dealt to a player by a source with infect doesn't cause that player to lose life. Rather, it causes the player to get that many poison counters. See rule 119.3. + * + * 702.87c. Damage dealt to a creature by a source with infect isn't marked on that creature. Rather, it causes that many -1/-1 counters to be put on that creature. See rule 119.3. + * + * 702.87d. If a permanent leaves the battlefield before an effect causes it to deal damage, its last known information + * (Last Known Information: Information about an object that's no longer in the zone it's expected to be in, or information about a player that's no longer in the game. This information captures that object's last existence in that zone or that player's last existence in the game....) + * 112.7a. Once activated or triggered, an ability exists on the stack independently of its source. Destruction or removal of the source after that time won't affect the ability. Note that some abilities cause a source to do something (for example, "Prodigal Sorcerer deals 1 damage... + * 608.2b. If the spell or ability specifies targets, it checks whether the targets are still legal. A target that's no longer in the zone it was in when it was targeted is illegal. Other changes to the game state may cause a target to no longer be legal; for example, its... + * 608.2g. If an effect requires information from the game (such as the number of creatures on the battlefield), the answer is determined only once, when the effect is applied. If the effect requires information from a specific object, including the source of the ability itself or a... + * 800.4f. If an effect requires information about a specific player, the effect uses the current information about that player if he or she is still in the game; otherwise, the effect uses the last known information about that player before he or she left the game. + * is used to determine whether it had infect. + * + * 702.87e. The infect rules function no matter what zone an object with infect deals damage from. + * + * 702.87f. Multiple instances of infect on the same object are redundant. + * + * @author nantuko + */ +public class InfectAbility extends StaticAbility { + + private static final InfectAbility fINSTANCE = new InfectAbility(); + + private Object readResolve() throws ObjectStreamException { + return fINSTANCE; + } + + public static InfectAbility getInstance() { + return fINSTANCE; + } + + private InfectAbility() { + super(Zone.ALL, null); + } + + @Override + public String getRule() { + return "Infect"; + } + + @Override + public InfectAbility copy() { + return fINSTANCE; + } + +} diff --git a/Mage/src/mage/abilities/keyword/WitherAbility.java b/Mage/src/mage/abilities/keyword/WitherAbility.java new file mode 100644 index 00000000000..53d05434cf0 --- /dev/null +++ b/Mage/src/mage/abilities/keyword/WitherAbility.java @@ -0,0 +1,75 @@ +/* +* 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.keyword; + +import mage.Constants.Zone; +import mage.abilities.StaticAbility; + +import java.io.ObjectStreamException; + +/** + * 702.77. Wither + * + * 702.77a. Wither is a static ability. Damage dealt to a creature by a source with wither isn't marked on that creature. Rather, it causes that many -1/-1 counters to be put on that creature. See rule 119.3. + * + * 702.77b. If a permanent leaves the battlefield before an effect causes it to deal damage, its last known information is used to determine whether it had wither. + * + * 702.77c. The wither rules function no matter what zone an object with wither deals damage from. + * + * 702.77d. Multiple instances of wither on the same object are redundant. + * + * @author nantuko + */ +public class WitherAbility extends StaticAbility { + + private static final WitherAbility fINSTANCE = new WitherAbility(); + + private Object readResolve() throws ObjectStreamException { + return fINSTANCE; + } + + public static WitherAbility getInstance() { + return fINSTANCE; + } + + private WitherAbility() { + super(Zone.ALL, null); + } + + @Override + public String getRule() { + return "Wither"; + } + + @Override + public WitherAbility copy() { + return fINSTANCE; + } + +} diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index a6c501f9fca..53dd3b887a5 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -38,16 +38,11 @@ import mage.abilities.Ability; import mage.abilities.EvasionAbility; import mage.abilities.TriggeredAbility; import mage.abilities.effects.RestrictionEffect; -import mage.abilities.keyword.DeathtouchAbility; -import mage.abilities.keyword.DefenderAbility; -import mage.abilities.keyword.HasteAbility; -import mage.abilities.keyword.IndestructibleAbility; -import mage.abilities.keyword.LifelinkAbility; -import mage.abilities.keyword.ProtectionAbility; -import mage.abilities.keyword.ShroudAbility; +import mage.abilities.keyword.*; import mage.cards.CardImpl; import mage.counters.Counter; import mage.counters.Counters; +import mage.counters.MinusOneCounter; import mage.game.Game; import mage.game.events.DamageCreatureEvent; import mage.game.events.DamagePlaneswalkerEvent; @@ -467,7 +462,13 @@ public abstract class PermanentImpl> extends CardImpl if (this.damage + event.getAmount() > this.toughness.getValue()) { actualDamage = this.toughness.getValue() - this.damage; } - this.damage += actualDamage; + Permanent source = game.getPermanent(sourceId); + if (source != null && (source.getAbilities().containsKey(InfectAbility.getInstance().getId()) + || source.getAbilities().containsKey(WitherAbility.getInstance().getId()))) { + addCounters(new MinusOneCounter(actualDamage)); + } else { + this.damage += actualDamage; + } game.fireEvent(new DamagedCreatureEvent(objectId, sourceId, controllerId, actualDamage, combat)); return actualDamage; } diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index e6b58264df9..ac433d94ee5 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -52,17 +52,16 @@ import mage.abilities.SpecialAction; import mage.abilities.SpellAbility; import mage.abilities.TriggeredAbility; import mage.abilities.common.PassAbility; -import mage.abilities.keyword.KickerAbility; -import mage.abilities.keyword.LifelinkAbility; -import mage.abilities.keyword.ProtectionAbility; -import mage.abilities.keyword.ShroudAbility; +import mage.abilities.keyword.*; import mage.abilities.mana.ManaAbility; import mage.abilities.mana.ManaOptions; import mage.cards.Card; import mage.cards.Cards; import mage.cards.CardsImpl; import mage.cards.decks.Deck; +import mage.counters.CounterType; import mage.counters.Counters; +import mage.counters.MinusOneCounter; import mage.filter.FilterAbility; import mage.filter.common.FilterCreatureForAttack; import mage.filter.common.FilterCreatureForCombat; @@ -626,8 +625,12 @@ public abstract class PlayerImpl> implements Player, Ser if (!game.replaceEvent(event)) { int actualDamage = event.getAmount(); if (actualDamage > 0) { - actualDamage = this.loseLife(actualDamage, game); Permanent source = game.getPermanent(sourceId); + if (source != null && (source.getAbilities().containsKey(InfectAbility.getInstance().getId()))) { + getCounters().addCounter(CounterType.POISON.getInstance()); + } else { + actualDamage = this.loseLife(actualDamage, game); + } if (source != null && source.getAbilities().containsKey(LifelinkAbility.getInstance().getId())) { Player player = game.getPlayer(source.getControllerId()); player.gainLife(actualDamage, game); From 6ba6d46f439a6528415601c21db568d4d0b8a5ac Mon Sep 17 00:00:00 2001 From: magenoxx Date: Wed, 29 Dec 2010 12:39:07 +0300 Subject: [PATCH 16/23] [mage.core] forgot to return value when no type was found. --- Mage/src/mage/counters/CounterType.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Mage/src/mage/counters/CounterType.java b/Mage/src/mage/counters/CounterType.java index 6447dcc172a..8adf29dd815 100644 --- a/Mage/src/mage/counters/CounterType.java +++ b/Mage/src/mage/counters/CounterType.java @@ -77,5 +77,6 @@ public enum CounterType { case POISON: return new PoisonCounter(amount); } + return null; } } From 3cb6693450a72c4e17fc2054d7695f2c5f1b811b Mon Sep 17 00:00:00 2001 From: Loki Date: Wed, 29 Dec 2010 11:44:03 +0200 Subject: [PATCH 17/23] Koth of the Hammer --- .../sets/scarsofmirrodin/KothoftheHammer.java | 222 ++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/KothoftheHammer.java diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/KothoftheHammer.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/KothoftheHammer.java new file mode 100644 index 00000000000..186db635b7c --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/KothoftheHammer.java @@ -0,0 +1,222 @@ +/* + * 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.scarsofmirrodin; + +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.LoyaltyAbility; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.common.TapSourceCost; +import mage.abilities.effects.ContinuousEffectImpl; +import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.ManaEffect; +import mage.abilities.effects.common.UntapTargetEffect; +import mage.cards.CardImpl; +import mage.filter.Filter; +import mage.filter.common.FilterLandPermanent; +import mage.game.Game; +import mage.game.permanent.Permanent; +import mage.target.common.TargetCreatureOrPlayer; +import mage.target.common.TargetLandPermanent; + +/** + * + * @author Loki + */ +public class KothoftheHammer extends CardImpl { + static FilterLandPermanent filter = new FilterLandPermanent("Mountain"); + + static { + filter.getSubtype().add("Mountain"); + filter.setScopeSubtype(Filter.ComparisonScope.Any); + } + + public KothoftheHammer (UUID ownerId) { + super(ownerId, 94, "Koth of the Hammer", Rarity.MYTHIC, new CardType[]{CardType.PLANESWALKER}, "{2}{R}{R}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Koth"); + this.color.setRed(true); + this.loyalty = new MageInt(3); + Ability ability = new LoyaltyAbility(new UntapTargetEffect(), 1); + ability.addEffect(new KothoftheHammerFirstEffect()); + ability.addTarget(new TargetLandPermanent(filter)); + this.addAbility(ability); + this.addAbility(new LoyaltyAbility(new KothoftheHammerSecondEffect(), -2)); + this.addAbility(new LoyaltyAbility(new KothoftheHammerThirdEffect(), -5)); + } + + public KothoftheHammer (final KothoftheHammer card) { + super(card); + } + + @Override + public KothoftheHammer copy() { + return new KothoftheHammer(this); + } +} + +class KothoftheHammerFirstEffect extends ContinuousEffectImpl { + + public KothoftheHammerFirstEffect() { + super(Duration.EndOfTurn, Constants.Outcome.BecomeCreature); + } + + public KothoftheHammerFirstEffect(final KothoftheHammerFirstEffect effect) { + super(effect); + } + + @Override + public boolean apply(Constants.Layer layer, Constants.SubLayer sublayer, Ability source, Game game) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + switch (layer) { + case TypeChangingEffects_4: + if (sublayer == Constants.SubLayer.NA) { + permanent.getCardType().add(CardType.CREATURE); + permanent.getSubtype().add("Elemental"); + } + break; + case ColorChangingEffects_5: + if (sublayer == Constants.SubLayer.NA) { + permanent.getColor().setRed(true); + } + break; + case PTChangingEffects_7: + if (sublayer == Constants.SubLayer.SetPT_7b) { + permanent.getPower().setValue(4); + permanent.getToughness().setValue(4); + } + } + return true; + } + return false; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public KothoftheHammerFirstEffect copy() { + return new KothoftheHammerFirstEffect(this); + } + + @Override + public boolean hasLayer(Constants.Layer layer) { + return layer == Constants.Layer.PTChangingEffects_7 || layer == Constants.Layer.ColorChangingEffects_5 || layer == layer.TypeChangingEffects_4; + } + + @Override + public String getText(Ability source) { + return "It becomes a 4/4 red Elemental creature until end of turn. It's still a land"; + } +} + +class KothoftheHammerSecondEffect extends OneShotEffect { + public KothoftheHammerSecondEffect() { + super(Constants.Outcome.PutManaInPool); + } + + public KothoftheHammerSecondEffect(final KothoftheHammerSecondEffect effect) { + super(effect); + } + + @Override + public boolean apply(Game game, Ability source) { + int count = game.getBattlefield().count(KothoftheHammer.filter, source.getControllerId(), game); + int current = game.getPlayer(source.getControllerId()).getManaPool().getRed(); + game.getPlayer(source.getControllerId()).getManaPool().setRed(count + current); + return true; + } + + @Override + public KothoftheHammerSecondEffect copy() { + return new KothoftheHammerSecondEffect(this); + } + + @Override + public String getText(Ability source) { + return "Add {R} to your mana pool for each Mountain you control"; + } +} + +class KothoftheHammerThirdEffect extends ContinuousEffectImpl { + public KothoftheHammerThirdEffect() { + super(Duration.EndOfGame, Constants.Outcome.AddAbility); + } + + public KothoftheHammerThirdEffect(final KothoftheHammerThirdEffect effect) { + super(effect); + } + + @Override + public boolean apply(Constants.Layer layer, Constants.SubLayer sublayer, Ability source, Game game) { + switch (layer) { + case AbilityAddingRemovingEffects_6: + if (sublayer == Constants.SubLayer.NA) { + for (Permanent p : game.getBattlefield().getActivePermanents(KothoftheHammer.filter, source.getControllerId(), game)) { + Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(1), new TapSourceCost()); + ability.addTarget(new TargetCreatureOrPlayer()); + p.addAbility(ability); + } + } + break; + } + return true; + } + + @Override + public boolean apply(Game game, Ability source) { + return false; + } + + @Override + public KothoftheHammerThirdEffect copy() { + return new KothoftheHammerThirdEffect(this); + } + + @Override + public boolean hasLayer(Constants.Layer layer) { + return layer == Constants.Layer.AbilityAddingRemovingEffects_6; + } + + @Override + public String getText(Ability source) { + return "You get an emblem with \"Mountains you control have '{T}: This land deals 1 damage to target creature or player.'\""; + } +} \ No newline at end of file From e3b9c8f20da8ab2d289c122dc873a2bea6ccca0a Mon Sep 17 00:00:00 2001 From: magenoxx Date: Wed, 29 Dec 2010 13:41:38 +0300 Subject: [PATCH 18/23] [mage.core] Setting poison counters fix. --- Mage/src/mage/players/PlayerImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index ac433d94ee5..774993df9ff 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -627,7 +627,7 @@ public abstract class PlayerImpl> implements Player, Ser if (actualDamage > 0) { Permanent source = game.getPermanent(sourceId); if (source != null && (source.getAbilities().containsKey(InfectAbility.getInstance().getId()))) { - getCounters().addCounter(CounterType.POISON.getInstance()); + getCounters().addCounter(CounterType.POISON.getInstance(actualDamage)); } else { actualDamage = this.loseLife(actualDamage, game); } From c425c4c3b6154c8fadc1fe1641b57898ecda4e3b Mon Sep 17 00:00:00 2001 From: magenoxx Date: Wed, 29 Dec 2010 13:42:05 +0300 Subject: [PATCH 19/23] [cards] Cystbearer (Infect) --- .../mage/sets/scarsofmirrodin/Cystbearer.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/Cystbearer.java diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Cystbearer.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Cystbearer.java new file mode 100644 index 00000000000..793d65af82f --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Cystbearer.java @@ -0,0 +1,70 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.scarsofmirrodin; + +import java.util.UUID; + +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.StaticAbility; +import mage.abilities.keyword.InfectAbility; +import mage.cards.CardImpl; + +/** + * Cystbearer + * + * @author nantuko + */ +public class Cystbearer extends CardImpl { + + public Cystbearer(UUID ownerId) { + super(ownerId, 117, "Cystbearer", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{G}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Beast"); + + this.color.setGreen(true); + this.power = new MageInt(2); + this.toughness = new MageInt(3); + + this.addAbility(InfectAbility.getInstance()); + } + + public Cystbearer(final Cystbearer card) { + super(card); + } + + @Override + public Cystbearer copy() { + return new Cystbearer(this); + } + +} From 93267f71320bd1249821e1da030c03b517de0d88 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Wed, 29 Dec 2010 14:41:03 +0300 Subject: [PATCH 20/23] More counter types (level,quest,charge,time,fade,...). Moved to common package. --- .../src/mage/sets/magic2010/ProteanHydra.java | 3 +- .../sets/zendikar/OranRiefTheVastwood.java | 2 +- .../AddPlusOneCountersAttachedEffect.java | 2 +- .../AddPlusOneCountersControlledEffect.java | 2 +- .../AddPlusOneCountersSourceEffect.java | 2 +- Mage/src/mage/counters/CounterType.java | 30 +++++++++++- .../counters/common/ArrowheadCounter.java | 49 +++++++++++++++++++ .../mage/counters/common/ChargeCounter.java | 49 +++++++++++++++++++ Mage/src/mage/counters/common/EonCounter.java | 49 +++++++++++++++++++ .../src/mage/counters/common/FadeCounter.java | 49 +++++++++++++++++++ .../mage/counters/common/FeatherCounter.java | 49 +++++++++++++++++++ .../mage/counters/common/LevelCounter.java | 49 +++++++++++++++++++ .../{ => common}/MinusOneCounter.java | 4 +- .../counters/{ => common}/PlusOneCounter.java | 4 +- .../counters/{ => common}/PoisonCounter.java | 4 +- .../mage/counters/common/QuestCounter.java | 49 +++++++++++++++++++ .../src/mage/counters/common/TimeCounter.java | 49 +++++++++++++++++++ .../mage/game/permanent/PermanentImpl.java | 3 +- Mage/src/mage/players/PlayerImpl.java | 1 - 19 files changed, 435 insertions(+), 14 deletions(-) create mode 100644 Mage/src/mage/counters/common/ArrowheadCounter.java create mode 100644 Mage/src/mage/counters/common/ChargeCounter.java create mode 100644 Mage/src/mage/counters/common/EonCounter.java create mode 100644 Mage/src/mage/counters/common/FadeCounter.java create mode 100644 Mage/src/mage/counters/common/FeatherCounter.java create mode 100644 Mage/src/mage/counters/common/LevelCounter.java rename Mage/src/mage/counters/{ => common}/MinusOneCounter.java (96%) rename Mage/src/mage/counters/{ => common}/PlusOneCounter.java (96%) rename Mage/src/mage/counters/{ => common}/PoisonCounter.java (94%) create mode 100644 Mage/src/mage/counters/common/QuestCounter.java create mode 100644 Mage/src/mage/counters/common/TimeCounter.java diff --git a/Mage.Sets/src/mage/sets/magic2010/ProteanHydra.java b/Mage.Sets/src/mage/sets/magic2010/ProteanHydra.java index f0a6917bb3b..3363f91a653 100644 --- a/Mage.Sets/src/mage/sets/magic2010/ProteanHydra.java +++ b/Mage.Sets/src/mage/sets/magic2010/ProteanHydra.java @@ -42,11 +42,10 @@ import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.PreventionEffectImpl; -import mage.abilities.effects.ReplacementEffectImpl; import mage.abilities.effects.common.AddPlusOneCountersSourceEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.cards.CardImpl; -import mage.counters.PlusOneCounter; +import mage.counters.common.PlusOneCounter; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.GameEvent.EventType; diff --git a/Mage.Sets/src/mage/sets/zendikar/OranRiefTheVastwood.java b/Mage.Sets/src/mage/sets/zendikar/OranRiefTheVastwood.java index 640a2a21b9c..2aed5cfeb64 100644 --- a/Mage.Sets/src/mage/sets/zendikar/OranRiefTheVastwood.java +++ b/Mage.Sets/src/mage/sets/zendikar/OranRiefTheVastwood.java @@ -40,7 +40,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.mana.GreenManaAbility; import mage.cards.CardImpl; -import mage.counters.PlusOneCounter; +import mage.counters.common.PlusOneCounter; import mage.filter.FilterPermanent; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage/src/mage/abilities/effects/common/AddPlusOneCountersAttachedEffect.java b/Mage/src/mage/abilities/effects/common/AddPlusOneCountersAttachedEffect.java index e8d55b02577..2b17b1b4c07 100644 --- a/Mage/src/mage/abilities/effects/common/AddPlusOneCountersAttachedEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddPlusOneCountersAttachedEffect.java @@ -31,7 +31,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.counters.PlusOneCounter; +import mage.counters.common.PlusOneCounter; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage/src/mage/abilities/effects/common/AddPlusOneCountersControlledEffect.java b/Mage/src/mage/abilities/effects/common/AddPlusOneCountersControlledEffect.java index 4d0a4e24ac6..b427b6cad14 100644 --- a/Mage/src/mage/abilities/effects/common/AddPlusOneCountersControlledEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddPlusOneCountersControlledEffect.java @@ -31,7 +31,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.counters.PlusOneCounter; +import mage.counters.common.PlusOneCounter; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; diff --git a/Mage/src/mage/abilities/effects/common/AddPlusOneCountersSourceEffect.java b/Mage/src/mage/abilities/effects/common/AddPlusOneCountersSourceEffect.java index 27d80dc0d32..2a8c8586f4c 100644 --- a/Mage/src/mage/abilities/effects/common/AddPlusOneCountersSourceEffect.java +++ b/Mage/src/mage/abilities/effects/common/AddPlusOneCountersSourceEffect.java @@ -31,7 +31,7 @@ package mage.abilities.effects.common; import mage.Constants.Outcome; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.counters.PlusOneCounter; +import mage.counters.common.PlusOneCounter; import mage.game.Game; import mage.game.permanent.Permanent; diff --git a/Mage/src/mage/counters/CounterType.java b/Mage/src/mage/counters/CounterType.java index 8adf29dd815..418c1b4a186 100644 --- a/Mage/src/mage/counters/CounterType.java +++ b/Mage/src/mage/counters/CounterType.java @@ -28,6 +28,8 @@ package mage.counters; +import mage.counters.common.*; + /** * Enum for counters, names and instances. * @@ -36,7 +38,15 @@ package mage.counters; public enum CounterType { P1P1(new PlusOneCounter().name), M1M1(new MinusOneCounter().name), - POISON(new PoisonCounter().name); + POISON(new PoisonCounter().name), + CHARGE(new ChargeCounter().name), + LEVEL(new LevelCounter().name), + TIME(new TimeCounter().name), + FADE(new FadeCounter().name), + FEATHER(new FeatherCounter().name), + QUEST(new QuestCounter().name), + ARROWHEAD(new ArrowheadCounter().name), + EON(new EonCounter().name); private String name; @@ -63,7 +73,7 @@ public enum CounterType { } /** - * Get instance of counter type with defined amount of the given type. + * Get instance of counter type with defined amount of counters of the given type. * * @param amount amount of counters of the given type. * @return @@ -76,6 +86,22 @@ public enum CounterType { return new MinusOneCounter(amount); case POISON: return new PoisonCounter(amount); + case CHARGE: + return new ChargeCounter(amount); + case LEVEL: + return new LevelCounter(amount); + case TIME: + return new TimeCounter(amount); + case FADE: + return new FadeCounter(amount); + case FEATHER: + return new FeatherCounter(amount); + case QUEST: + return new QuestCounter(amount); + case ARROWHEAD: + return new ArrowheadCounter(amount); + case EON: + return new EonCounter(amount); } return null; } diff --git a/Mage/src/mage/counters/common/ArrowheadCounter.java b/Mage/src/mage/counters/common/ArrowheadCounter.java new file mode 100644 index 00000000000..c6972f11832 --- /dev/null +++ b/Mage/src/mage/counters/common/ArrowheadCounter.java @@ -0,0 +1,49 @@ +/* +* 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.counters.common; + +import mage.counters.Counter; + +/** + * Arrowhead counter. + * + * @author nantuko + */ +public class ArrowheadCounter extends Counter { + + public ArrowheadCounter() { + super("Arrowhead"); + this.count = 1; + } + + public ArrowheadCounter(int amount) { + super("Arrowhead"); + this.count = amount; + } +} diff --git a/Mage/src/mage/counters/common/ChargeCounter.java b/Mage/src/mage/counters/common/ChargeCounter.java new file mode 100644 index 00000000000..474355e52a2 --- /dev/null +++ b/Mage/src/mage/counters/common/ChargeCounter.java @@ -0,0 +1,49 @@ +/* +* 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.counters.common; + +import mage.counters.Counter; + +/** + * Charge counter. + * + * @author nantuko + */ +public class ChargeCounter extends Counter { + + public ChargeCounter() { + super("Charge"); + this.count = 1; + } + + public ChargeCounter(int amount) { + super("Charge"); + this.count = amount; + } +} \ No newline at end of file diff --git a/Mage/src/mage/counters/common/EonCounter.java b/Mage/src/mage/counters/common/EonCounter.java new file mode 100644 index 00000000000..fb3df296712 --- /dev/null +++ b/Mage/src/mage/counters/common/EonCounter.java @@ -0,0 +1,49 @@ +/* +* 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.counters.common; + +import mage.counters.Counter; + +/** + * Eon counter. + * + * @author nantuko + */ +public class EonCounter extends Counter { + + public EonCounter() { + super("Eon"); + this.count = 1; + } + + public EonCounter(int amount) { + super("Eon"); + this.count = amount; + } +} diff --git a/Mage/src/mage/counters/common/FadeCounter.java b/Mage/src/mage/counters/common/FadeCounter.java new file mode 100644 index 00000000000..5684054fb05 --- /dev/null +++ b/Mage/src/mage/counters/common/FadeCounter.java @@ -0,0 +1,49 @@ +/* +* 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.counters.common; + +import mage.counters.Counter; + +/** + * Fade counter. + * + * @author nantuko + */ +public class FadeCounter extends Counter { + + public FadeCounter() { + super("Fade"); + this.count = 1; + } + + public FadeCounter(int amount) { + super("Fade"); + this.count = amount; + } +} diff --git a/Mage/src/mage/counters/common/FeatherCounter.java b/Mage/src/mage/counters/common/FeatherCounter.java new file mode 100644 index 00000000000..3face1d12e3 --- /dev/null +++ b/Mage/src/mage/counters/common/FeatherCounter.java @@ -0,0 +1,49 @@ +/* +* 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.counters.common; + +import mage.counters.Counter; + +/** + * Feather counter. + * + * @author nantuko + */ +public class FeatherCounter extends Counter { + + public FeatherCounter() { + super("Feather"); + this.count = 1; + } + + public FeatherCounter(int amount) { + super("Feather"); + this.count = amount; + } +} diff --git a/Mage/src/mage/counters/common/LevelCounter.java b/Mage/src/mage/counters/common/LevelCounter.java new file mode 100644 index 00000000000..16282b063c7 --- /dev/null +++ b/Mage/src/mage/counters/common/LevelCounter.java @@ -0,0 +1,49 @@ +/* +* 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.counters.common; + +import mage.counters.Counter; + +/** + * Level counter. + * + * @author nantuko + */ +public class LevelCounter extends Counter { + + public LevelCounter() { + super("Level"); + this.count = 1; + } + + public LevelCounter(int amount) { + super("Level"); + this.count = amount; + } +} diff --git a/Mage/src/mage/counters/MinusOneCounter.java b/Mage/src/mage/counters/common/MinusOneCounter.java similarity index 96% rename from Mage/src/mage/counters/MinusOneCounter.java rename to Mage/src/mage/counters/common/MinusOneCounter.java index ccaaef28055..0e8e4a842bc 100644 --- a/Mage/src/mage/counters/MinusOneCounter.java +++ b/Mage/src/mage/counters/common/MinusOneCounter.java @@ -26,7 +26,9 @@ * or implied, of BetaSteward_at_googlemail.com. */ -package mage.counters; +package mage.counters.common; + +import mage.counters.BoostCounter; /** * diff --git a/Mage/src/mage/counters/PlusOneCounter.java b/Mage/src/mage/counters/common/PlusOneCounter.java similarity index 96% rename from Mage/src/mage/counters/PlusOneCounter.java rename to Mage/src/mage/counters/common/PlusOneCounter.java index 1e1844c7c79..6fb6c72c358 100644 --- a/Mage/src/mage/counters/PlusOneCounter.java +++ b/Mage/src/mage/counters/common/PlusOneCounter.java @@ -26,7 +26,9 @@ * or implied, of BetaSteward_at_googlemail.com. */ -package mage.counters; +package mage.counters.common; + +import mage.counters.BoostCounter; /** * diff --git a/Mage/src/mage/counters/PoisonCounter.java b/Mage/src/mage/counters/common/PoisonCounter.java similarity index 94% rename from Mage/src/mage/counters/PoisonCounter.java rename to Mage/src/mage/counters/common/PoisonCounter.java index bbcf8dab39b..70e3a22740a 100644 --- a/Mage/src/mage/counters/PoisonCounter.java +++ b/Mage/src/mage/counters/common/PoisonCounter.java @@ -26,7 +26,9 @@ * or implied, of BetaSteward_at_googlemail.com. */ -package mage.counters; +package mage.counters.common; + +import mage.counters.Counter; /** * Poison counter. diff --git a/Mage/src/mage/counters/common/QuestCounter.java b/Mage/src/mage/counters/common/QuestCounter.java new file mode 100644 index 00000000000..18c3dc0dd95 --- /dev/null +++ b/Mage/src/mage/counters/common/QuestCounter.java @@ -0,0 +1,49 @@ +/* +* 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.counters.common; + +import mage.counters.Counter; + +/** + * Quest counter. + * + * @author nantuko + */ +public class QuestCounter extends Counter { + + public QuestCounter() { + super("Quest"); + this.count = 1; + } + + public QuestCounter(int amount) { + super("Quest"); + this.count = amount; + } +} diff --git a/Mage/src/mage/counters/common/TimeCounter.java b/Mage/src/mage/counters/common/TimeCounter.java new file mode 100644 index 00000000000..040294a7522 --- /dev/null +++ b/Mage/src/mage/counters/common/TimeCounter.java @@ -0,0 +1,49 @@ +/* +* 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.counters.common; + +import mage.counters.Counter; + +/** + * Time counter. + * + * @author nantuko + */ +public class TimeCounter extends Counter { + + public TimeCounter() { + super("Time"); + this.count = 1; + } + + public TimeCounter(int amount) { + super("Time"); + this.count = amount; + } +} \ No newline at end of file diff --git a/Mage/src/mage/game/permanent/PermanentImpl.java b/Mage/src/mage/game/permanent/PermanentImpl.java index 53dd3b887a5..722ad28129f 100644 --- a/Mage/src/mage/game/permanent/PermanentImpl.java +++ b/Mage/src/mage/game/permanent/PermanentImpl.java @@ -35,14 +35,13 @@ import mage.Constants.CardType; import mage.Constants.Zone; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.EvasionAbility; import mage.abilities.TriggeredAbility; import mage.abilities.effects.RestrictionEffect; import mage.abilities.keyword.*; import mage.cards.CardImpl; import mage.counters.Counter; import mage.counters.Counters; -import mage.counters.MinusOneCounter; +import mage.counters.common.MinusOneCounter; import mage.game.Game; import mage.game.events.DamageCreatureEvent; import mage.game.events.DamagePlaneswalkerEvent; diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 774993df9ff..bd381ef8bf9 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -61,7 +61,6 @@ import mage.cards.CardsImpl; import mage.cards.decks.Deck; import mage.counters.CounterType; import mage.counters.Counters; -import mage.counters.MinusOneCounter; import mage.filter.FilterAbility; import mage.filter.common.FilterCreatureForAttack; import mage.filter.common.FilterCreatureForCombat; From a4f3e4f97c0d27374888540b50fdf78fa6dc9481 Mon Sep 17 00:00:00 2001 From: magenoxx Date: Wed, 29 Dec 2010 14:41:12 +0300 Subject: [PATCH 21/23] Refactored cards with charge counters. --- Mage.Sets/src/mage/sets/scarsofmirrodin/LuxCannon.java | 5 +++-- Mage.Sets/src/mage/sets/worldwake/EverflowingChalice.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/LuxCannon.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/LuxCannon.java index 78f1095b1d7..dba7f75735a 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/LuxCannon.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/LuxCannon.java @@ -38,6 +38,7 @@ import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.common.AddCountersSourceEffect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; +import mage.counters.CounterType; import mage.target.TargetPermanent; import java.util.UUID; @@ -51,9 +52,9 @@ public class LuxCannon extends CardImpl { public LuxCannon (UUID ownerId) { super(ownerId, 173, "Lux Cannon", Rarity.MYTHIC, new CardType[]{CardType.ARTIFACT}, "{4}"); this.expansionSetCode = "SOM"; - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect("charge", 1), new TapSourceCost())); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.CHARGE.getName(), 1), new TapSourceCost())); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new TapSourceCost()); - ability.addCost(new RemoveCountersSourceCost("charge", 3)); + ability.addCost(new RemoveCountersSourceCost(CounterType.CHARGE.getName(), 3)); ability.addTarget(new TargetPermanent()); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/sets/worldwake/EverflowingChalice.java b/Mage.Sets/src/mage/sets/worldwake/EverflowingChalice.java index 23dabfcfff7..c503a68de3f 100644 --- a/Mage.Sets/src/mage/sets/worldwake/EverflowingChalice.java +++ b/Mage.Sets/src/mage/sets/worldwake/EverflowingChalice.java @@ -41,6 +41,7 @@ import mage.abilities.effects.common.ManaEffect; import mage.abilities.keyword.MultikickerAbility; import mage.abilities.mana.ManaAbility; import mage.cards.CardImpl; +import mage.counters.CounterType; import mage.game.Game; /** @@ -52,7 +53,7 @@ public class EverflowingChalice extends CardImpl { public EverflowingChalice(UUID ownerId) { super(ownerId, 123, "Everflowing Chalice", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{0}"); this.expansionSetCode = "WWK"; - MultikickerAbility ability = new MultikickerAbility(new AddCountersSourceEffect("charge", 1), false); + MultikickerAbility ability = new MultikickerAbility(new AddCountersSourceEffect(CounterType.CHARGE.getName(), 1), false); ability.addManaCost(new GenericManaCost(2)); this.addAbility(ability); this.addAbility(new EverflowingChaliceAbility()); @@ -116,7 +117,7 @@ class EverflowingChaliceEffect extends ManaEffect { @Override public boolean apply(Game game, Ability source) { this.mana.clear(); - this.mana.setColorless(game.getPermanent(source.getSourceId()).getCounters().getCount("charge")); + this.mana.setColorless(game.getPermanent(source.getSourceId()).getCounters().getCount(CounterType.CHARGE)); return super.apply(game, source); } From cd1d381ade6054eb4c30af01c06b446ace07d040 Mon Sep 17 00:00:00 2001 From: Loki Date: Wed, 29 Dec 2010 17:49:24 +0200 Subject: [PATCH 22/23] SOM --- .../sets/scarsofmirrodin/BlightMamba.java | 70 +++++++++++++++++ .../sets/scarsofmirrodin/ContagiousNim.java | 63 +++++++++++++++ .../sets/scarsofmirrodin/PlagueStinger.java | 71 +++++++++++++++++ .../mage/sets/scarsofmirrodin/Putrefax.java | 71 +++++++++++++++++ .../SkithiryxtheBlightDragon.java | 76 +++++++++++++++++++ .../sets/scarsofmirrodin/TaintedStrike.java | 66 ++++++++++++++++ 6 files changed, 417 insertions(+) create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/BlightMamba.java create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/ContagiousNim.java create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/PlagueStinger.java create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/Putrefax.java create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/SkithiryxtheBlightDragon.java create mode 100644 Mage.Sets/src/mage/sets/scarsofmirrodin/TaintedStrike.java diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/BlightMamba.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/BlightMamba.java new file mode 100644 index 00000000000..dd3c4a6b66c --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/BlightMamba.java @@ -0,0 +1,70 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.scarsofmirrodin; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.abilities.keyword.InfectAbility; +import mage.cards.CardImpl; +import mage.sets.magic2011.InfernoTitan; + +/** + * + * @author Loki + */ +public class BlightMamba extends CardImpl { + + public BlightMamba (UUID ownerId) { + super(ownerId, 112, "Blight Mamba", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{G}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Snake"); + this.color.setGreen(true); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + this.addAbility(InfectAbility.getInstance()); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{1}{G}"))); + } + + public BlightMamba (final BlightMamba card) { + super(card); + } + + @Override + public BlightMamba copy() { + return new BlightMamba(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/ContagiousNim.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/ContagiousNim.java new file mode 100644 index 00000000000..53a907a18fb --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/ContagiousNim.java @@ -0,0 +1,63 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.scarsofmirrodin; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Rarity; +import mage.MageInt; +import mage.abilities.keyword.InfectAbility; +import mage.cards.CardImpl; + +/** + * + * @author Loki + */ +public class ContagiousNim extends CardImpl { + + public ContagiousNim (UUID ownerId) { + super(ownerId, 58, "Contagious Nim", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{B}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Zombie"); + this.color.setBlack(true); + this.power = new MageInt(2); + this.toughness = new MageInt(2); + this.addAbility(InfectAbility.getInstance()); + } + + public ContagiousNim (final ContagiousNim card) { + super(card); + } + + @Override + public ContagiousNim copy() { + return new ContagiousNim(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/PlagueStinger.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/PlagueStinger.java new file mode 100644 index 00000000000..e95bea1e8b3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/PlagueStinger.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.scarsofmirrodin; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.common.OnEventTriggeredAbility; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.InfectAbility; +import mage.cards.CardImpl; +import mage.game.events.GameEvent; + +/** + * + * @author Loki + */ +public class PlagueStinger extends CardImpl { + + public PlagueStinger (UUID ownerId) { + super(ownerId, 75, "Plague Stinger", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{1}{B}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Insect"); + this.subtype.add("Horror"); + this.color.setBlack(true); + this.power = new MageInt(1); + this.toughness = new MageInt(1); + this.addAbility(FlyingAbility.getInstance()); + this.addAbility(InfectAbility.getInstance()); + } + + public PlagueStinger (final PlagueStinger card) { + super(card); + } + + @Override + public PlagueStinger copy() { + return new PlagueStinger(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Putrefax.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Putrefax.java new file mode 100644 index 00000000000..d9fa2662dfb --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Putrefax.java @@ -0,0 +1,71 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.scarsofmirrodin; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.common.OnEventTriggeredAbility; +import mage.abilities.effects.common.SacrificeSourceEffect; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.TrampleAbility; +import mage.cards.CardImpl; +import mage.game.events.GameEvent; + +/** + * + * @author Loki + */ +public class Putrefax extends CardImpl { + + public Putrefax (UUID ownerId) { + super(ownerId, 126, "Putrefax", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + this.expansionSetCode = "SOM"; + this.subtype.add("Horror"); + this.color.setGreen(true); + this.power = new MageInt(5); + this.toughness = new MageInt(3); + this.addAbility(TrampleAbility.getInstance()); + this.addAbility(HasteAbility.getInstance()); + this.addAbility(new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", new SacrificeSourceEffect())); + } + + public Putrefax (final Putrefax card) { + super(card); + } + + @Override + public Putrefax copy() { + return new Putrefax(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/SkithiryxtheBlightDragon.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/SkithiryxtheBlightDragon.java new file mode 100644 index 00000000000..89c004f98ca --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/SkithiryxtheBlightDragon.java @@ -0,0 +1,76 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.scarsofmirrodin; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.MageInt; +import mage.abilities.common.SimpleActivatedAbility; +import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.effects.common.GainAbilitySourceEffect; +import mage.abilities.effects.common.RegenerateSourceEffect; +import mage.abilities.keyword.FlyingAbility; +import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.InfectAbility; +import mage.cards.CardImpl; + +/** + * + * @author Loki + */ +public class SkithiryxtheBlightDragon extends CardImpl { + + public SkithiryxtheBlightDragon (UUID ownerId) { + super(ownerId, 79, "Skithiryx, the Blight Dragon", Rarity.MYTHIC, new CardType[]{CardType.CREATURE}, "{3}{B}{B}"); + this.expansionSetCode = "SOM"; + this.supertype.add("Legendary"); + this.subtype.add("Dragon"); + this.subtype.add("Skeleton"); + this.color.setBlack(true); + this.power = new MageInt(4); + this.toughness = new MageInt(4); + this.addAbility(FlyingAbility.getInstance()); + this.addAbility(InfectAbility.getInstance()); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{B}"))); + this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new RegenerateSourceEffect(), new ManaCostsImpl("{B}{B}"))); + } + + public SkithiryxtheBlightDragon (final SkithiryxtheBlightDragon card) { + super(card); + } + + @Override + public SkithiryxtheBlightDragon copy() { + return new SkithiryxtheBlightDragon(this); + } + +} diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/TaintedStrike.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/TaintedStrike.java new file mode 100644 index 00000000000..7129b69ebb6 --- /dev/null +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/TaintedStrike.java @@ -0,0 +1,66 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ + +package mage.sets.scarsofmirrodin; + +import java.util.UUID; +import mage.Constants.CardType; +import mage.Constants.Duration; +import mage.Constants.Rarity; +import mage.Constants.Zone; +import mage.abilities.effects.common.BoostTargetEffect; +import mage.abilities.effects.common.GainAbilityTargetEffect; +import mage.abilities.keyword.InfectAbility; +import mage.cards.CardImpl; +import mage.target.common.TargetCreaturePermanent; + +/** + * + * @author Loki + */ +public class TaintedStrike extends CardImpl { + + public TaintedStrike (UUID ownerId) { + super(ownerId, 80, "Tainted Strike", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{B}"); + this.expansionSetCode = "SOM"; + this.color.setBlack(true); + this.getSpellAbility().addEffect(new BoostTargetEffect(1, 0, Duration.EndOfTurn)); + this.getSpellAbility().addEffect(new GainAbilityTargetEffect(InfectAbility.getInstance(), Duration.EndOfTurn)); + this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + } + + public TaintedStrike (final TaintedStrike card) { + super(card); + } + + @Override + public TaintedStrike copy() { + return new TaintedStrike(this); + } + +} From 7b62bbd24def86288f93fb4c5926b2203a9dc22e Mon Sep 17 00:00:00 2001 From: Loki Date: Wed, 29 Dec 2010 19:32:36 +0200 Subject: [PATCH 23/23] fix Putrefax --- Mage.Sets/src/mage/sets/scarsofmirrodin/Putrefax.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Mage.Sets/src/mage/sets/scarsofmirrodin/Putrefax.java b/Mage.Sets/src/mage/sets/scarsofmirrodin/Putrefax.java index d9fa2662dfb..96d89943265 100644 --- a/Mage.Sets/src/mage/sets/scarsofmirrodin/Putrefax.java +++ b/Mage.Sets/src/mage/sets/scarsofmirrodin/Putrefax.java @@ -30,13 +30,11 @@ package mage.sets.scarsofmirrodin; import java.util.UUID; import mage.Constants.CardType; -import mage.Constants.Duration; import mage.Constants.Rarity; -import mage.Constants.Zone; import mage.MageInt; import mage.abilities.common.OnEventTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; -import mage.abilities.keyword.HasteAbility; +import mage.abilities.keyword.InfectAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.game.events.GameEvent; @@ -55,7 +53,7 @@ public class Putrefax extends CardImpl { this.power = new MageInt(5); this.toughness = new MageInt(3); this.addAbility(TrampleAbility.getInstance()); - this.addAbility(HasteAbility.getInstance()); + this.addAbility(InfectAbility.getInstance()); this.addAbility(new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", new SacrificeSourceEffect())); }