From 6ae4ac3c5e93ee4b2a1eb7be391fe87bc4081446 Mon Sep 17 00:00:00 2001 From: BetaSteward <betasteward@gmail> Date: Sun, 26 Dec 2010 00:34:34 -0500 Subject: [PATCH] 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 b18a86b854..09d9c8f9e5 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<String> playerTypes = new ArrayList<String>(); - 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 af7841411d..1093013070 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<String> 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 94f01af53e..929c0facab 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<String> playerTypes = new ArrayList<String>(); - 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 5ce8de4b8a..18ba540d47 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<String> 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 97426e47f9..310e405727 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 478dcb5b93..97c25e4c5f 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<FreeForAll> { } @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 0000000000..613fcf6af7 --- /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<FreeForAll> { + + 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 ee24d0675a..a7fabda172 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<FreeForAllType> { 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 fe3b9d76fe..7c53f6eab1 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<TwoPlayerDuel> { 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<TwoPlayerDuel> { } @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 c25aaa8dc2..c15c12f1e4 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<TwoPlayerDuelType> { 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 0000000000..c3f7723146 --- /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<TwoPlayerDuel> { + + 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 9733c9db7e..df6998b9b9 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -9,8 +9,8 @@ <playerType name="Computer - minimax hybrid" jar="mage-player-aiminimax.jar" className="mage.player.ai.ComputerPlayer3"/> </playerTypes> <gameTypes> - <gameType name="Two Player Duel" jar="mage-game-twoplayerduel.jar" className="mage.game.TwoPlayerDuel" typeName="mage.game.TwoPlayerDuelType"/> - <gameType name="Free For All" jar="mage-game-freeforall.jar" className="mage.game.FreeForAll" typeName="mage.game.FreeForAllType"/> + <gameType name="Two Player Duel" jar="mage-game-twoplayerduel.jar" className="mage.game.TwoPlayerMatch" typeName="mage.game.TwoPlayerDuelType"/> + <gameType name="Free For All" jar="mage-game-freeforall.jar" className="mage.game.FreeForAllMatch" typeName="mage.game.FreeForAllType"/> </gameTypes> <deckTypes> <deckType name="Constructed" jar="mage-deck-constructed.jar" className="mage.deck.Constructed"/> 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?l<y7MOm* zY7C~O*gSbfwoJ8^GIsF$%E-X*W^w_WcYVrTiG!a{pGvrp)4Vo0-*s|+%+_rwDwdv- zf8S45J$hen;RN>AFJ8#qom?!cGF61%p|nDad!60dcU#wOlbHU5L1tptg=z<<XFraA zm)+-N*cINl(^m7^rvBr~R{J*Z@s(NsasJ)2{>2+q1Rr||`z4CDEVSGhDz;B>N`~4G z)}n_(^@o`*K76IOulu5B>gp>VGvX!GmY!JfgEvUB^GcZK2ZrpKq7x64W{HcPSh`~M z`p7Ji0M8<i50Wn}T(Y*Qhe&T&=n}*+_krB4K>j!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)F7w3w<y4FYo%n>M49r(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|_Q8K6yKbb3O0bja<zJ0u2vs z3*Hwz$mwv8VObvL#jgIF>3Rik(5xxOT(!T`{<i(k&zt13^F-Hq`Exa1OqX?HK4>wn zbYFDvccYEpY37rqd}lOw<y-ci`?if)*i`=hZ>zN4Z-)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!<t zS@ck-{xH+Uf3FPddM*a0uD;?iBVIyn>4^mu3@dwFvR129G;BT7<?+C3D>wI(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@eISM<dBwa}Z_Iy+3xh6@*Io$3-*P%f{yxvQ}@ zTlmk{o@CvLT>FwMb?e`1_<gU|TPptI@yR)?i`0zR-*)_~Ue)nmG4qgh^0Q5cg{Q0R z;9m7P$7*@Cu;N71%O(FmdzZ|8Q1*BKTGnKbug~s3vOebdajT^7?`<h(g&lq;-xjR7 zbvxm=xMLTW&*KFJQWBEpPMIIXVF`!>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^<PY>K%wf1b{VREGc7Fz>blm2dzqam zFt%;3P<D>fi_>F3)p<sN8DzkFRS>mVoN*^3h%xyxlNN}c#B2=anlZZyN^Y5ID?2+U z@e?Bh!vj_Z2E)mG%+iw&a7fnA4e{;1Z6IQEegC1BI9<z^+onADwMB%>Gx`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<$FrOx<qg!hsr`G`1g>z2!Z=Xt-Bs@*YTv%?jaXA5L6?)Mk{$URAI zhjE&owN0UC)9wYk&MnK{WiBWbY3#YbuCC8cZAa|>ZCev~R^Gc4{pWy%=03B<ng6vS zjei|YP1)@`|BKm`fBp)ttjjV3EO>ibr01Lb;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*!1rr<RBk4;))AJDek#Pn2sbZv(0(u@x$`?5=dm1Y8Y zoReGG)xf-UK+*sIk4`?tE(hknW|w67|Nj%yWPWBDUR2u+IY3ex%s?qAU{V*FT*bko efRPb^j(~wBjRi2ps=O%SGWionEL#N|NFe~lfsKy< delta 999 zcmcaEbXABqz?+$civa`z<C`b)s_+KHH{1T*(O;bg6snnMm!bAla;c@IK%+?P`YAaR zUZz#CeQbNSCuUC!1FF{33d|rA-l>A9&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^r4AC3C<vBKqe_UY*3A4UJ|y?t+XvudYAefX>b 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&-Io<?!|0-9!4a79XDfU45SS(}~+X!NtC3rA-Z1uYNmm`^55d`#;K7cjO)` z|FJyH_5WmzKyR+?K2nT|@j<GYO~(`5*LU5jSmhR8`a)pNljH4IRj*%<EXbU-TJ28A z6j5ylw|Dg)HkC5}zNYr!wdC<ACDEGmpQ3IF&UyDe#%ukB<C`tHU3^pcZVCSV9U}PO z`4UU{u`{Xd>Uy39;vv&-mUw<wb5hDyd-=Ry|Kf6%?XUmm>Pk4TGXE}i-`KBfcbdst zd(~{W{TBT>+h0z;*LLea=aLl%OnAALH%=^gJejSY5tNi9K*^8;lniZ?cpfgy2l5Jm z*aDn5CjVqr5h>F?6;j~o8)je-W?-_#XzK<ev!Ka_Y@&=$CwsCf$*`rq{+049jY;?6 zQzl^IWMmRyhNtYw?QABfNqh1MHd9cFoWw3|vL9$qZen_>KAJ9Dpg0^r%mQ%*7~VQQ zn(WUm2~N@3?9z-}liS(Vz`PCYk}Pi>k1<Vt$Rs@Z0Xr91@GV&IGZQeGOKIb?Lkn&j p$PUfP{v4L_n7IY6h+#=%AyAF(<SQJ)yeI)T`3pxZTNxWjB>>*#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 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 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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 diff --git a/Mage.Server/plugins/mage-player-ai.jar b/Mage.Server/plugins/mage-player-ai.jar index e7b4c9190c35ad34ca822c515cebd7a345ae250c..da0274094b85c257c15a88717a827d99caf1bdd6 100644 GIT binary patch delta 1068 zcmaF&knz()M&1B#W)?065a``Hb0V(_a}SU*nF6LxD}bqY>dZj7$r6lOVA_?@6hv>X zWqiaW2xQoL$mPU{urM&hb1*QdOy0;Xy1A9bfCZ#=^A3&|Okl=h?%hmaU5R|r7GTDH zw+M)cl&1&_Sj5oRo)yd}4)W#zGY&=xK^WJP)-r=cCby+Q9I-jg7|ef}<|L@KWvXq_ z{=@%1FfuUQW@TW|n|v@$dUAY$SpD3P*zPL^BDLH0FXGu}_OfU72?6dV4sPpR3$*v? z8lElLyv@_&(octP`)8}otP5vb$ba_aN!#!9=9xGJb*6jy)U5JaXTN4%&%|gB?V1HL z37sc}S)D{b{CmxJuR&yq_W3*WOtNnte=I0--{PK{@AVJQ-@Th{8}YEEFxNo6MD*y! zdfSax`SuC3NUQx2E&3>QnDOMpXNI*t2R)y!KI3&my)RI;DC*w=ujtM*p^gO&TYWl3 zpQ;sevx|6L`DAu}4bzGON5MS`vh3Q2zs-=~Hk;zYnJDmPq3l-ymf#O!YXogdew7LH zU0q!GF*f+mz9Z?YCiQO*X1#6uvzPl=_Z!L8%kI>tzT9rPZiTkcZ>7TEBaIie&!1Si z#Af*%gH_eB+du4NW`DQmpKuT}(<h_Hm+x47Hsf9MDvSL|>XX^u^X??`7anfmtiQVE zO}&6_mg@`kWosG&p8IGwJ&<_wV$+(n3#+cIa&Ld2Vyn2yZu`P>P2QJ7OfRY}nzUxi z{d@9p+wv0bA3GLSYy3FETczoJWT19A<MQ9O(>Iw`lpCfuW^B+}@$tw9yU>>Z6Sl0J zYgl~qVW)T3FSf6xX0gV5dncTjo&Dv*-{Mbl2h0BMU+kK!!LsV`_nya}yo*im%DYas z&y<m2f+buIP{OrUI52BZJ}?m%0I?}F{)H!>E8v>EK2tp1PbaXz(>Kh(DAd4clab*@ zBcs3qUcNvBBLkBMPqn;twa%S6zuEMH@m1p|PrdX{>Fa***?3mp>-uS5AOELMy>zrr z`gr^3F>?K4Vl81V64DOtE|ImZ+{qB&&B!Fe49`B3wX#e=seN){mOYqWoMix}uVtBo zQ>#F>6`1zTb_LT@vpvD|!)$-BI^&!GFx>{EL8<*_jvtuT$_)n74Y@&J`f6?zm^R9b z2h&sXApDPcF<^c`KE$5hd}lEKa()mmN-mjfRNya<5pJMT01B2g#!g;P;K3%J1rh)N 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 z<cqcd8?oOlf(gu!@)ThKGYoz0S;36rAa4#Z<6x8!gmEotEi=fR$!%#`V0v?!F_?at z<|J6TyxF#-!*s%XMh1qPtPBi#lMkjzPu^c3Twjr!GdJXR)*S<pnsxgR>C|q0l5;so z>}r5UtHU<lEt<95tJaiGxix9Rlf40--cMCs`<~DH0ROd&l4o;1o)DbmWfPe6C^@v~ zZ|2vGo;wZ^4{e_^c>Bm{3an}<zrX#1)Dnf*%4g43M4q;j;d}7m+{bH~Rjl>-`SmxN zMg1hMvlTnlhX=~s7M6bdAlh+5VpF_=xW1vxgg5KuFSU!vl<qsVrlt0gM5xm8r~0e5 zOg#}J%EBITdP+oKp3(LKhlMV>`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*<D#&4zkI(}>!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_-fKoke<hcz7#zo(k&jGXDHIhxdz5<<3jppS-zkf^$B1U;0`kd^xysr%<<- z(Pg2@-VIIr92oC5%sF!Eic+k*f1DmefHxzP2s1qQOxDXX0cD@bsaf`*q&s<emI0W) zm1Pd5MY65HbYQkCn4X#K38tTB`-2m*c}@VB?t;>HbNs;adbz=1x+OOVOyA6n0@G%B z@nCvJ9)$ljF9xhXB;N{5Pt11))7SHZcu_LQWU~T)1&ja#6#`JOq%kIQ^16H}U=Gq{ Jlg<K(0svi`#V7y( diff --git a/Mage.Server/plugins/mage-player-aiminimax.jar b/Mage.Server/plugins/mage-player-aiminimax.jar index d2f5a8333b1dd8f0b52ddee13c94fbdd6bbeb860..c3a0c972fd3080e70c5d5a6ca52546c28e5ee855 100644 GIT binary patch delta 551 zcmew|hv~~4Cf)#VW)?065a`=Fb0V(_b1#rGnZgX>omK!-@6<umWC=zs5WU%z@evc4 zp~Z5Pi5DnhYyT@smVudpp?@;JvC3uzc420)ycyRS4KRb%SQ8>*Xci<37HJDMWCb&J zMSS1_Gu9?YKuqOG+r`8OGF3D$X{wMg14FC!WJfO1$?}ctlY00z|H_aN25XS4dCUT4 z{BM{I);W1@GuVNXPc<8ZBsR4yX5s)DWIW?f<K)$CLG=ay3m)WjI9hY?wN85^pm9Tf z$17776V3D~zrKI%U0+d<vha-XKK_y|#VnybQ$8M3zQwjS_w}x=;(Pe@w=6SSP&UnU z$qmW7FSw>m-%|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;$Z5I<C$W+n1q^Ux}3=FN>lO4H4C(AdoPwL^@{3}C77_33E<}nMH z@xNg<Sm)%q&0q&kKGkdtlGxO;n27^q(6(Lk>?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%<E0l74IJFXjMmMkWzvc(~ev z19);xyB;WbC$DX{2RU%^`*s5`t<+%-rVsW=PA=_G1hZ#$c!KGt9f4rlvJ*o0cKU<) z_n~~_E(pK1%MC1kt}6&kD|ZKj>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}<wv+3B@#ECT zYo419*>C$E|Mp;)j^y>W0<Lb2zPFvdWgqq~IAU-xe?s?jiF&>Wxp(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^ZCaFJ<wl~zxm~~ceyPt_s*Puyq*8_cFnx&|8GZ` zv^g!XsyS|<X>ZQ_@}Jrz{y+Elv$ji4y4PlPX1|b7&gBJ#ldH8Kv1@eyV}d0y4p0)a z74m<+y8xKnih<Yyh<ztdRuPA$2fljVGaI>@4Fni2*iNXoC@?$XY|W7;_EtgU4S&p6 z9w*-?Jx%rZ*W_139-r5$bpFHM#0A}Z!)|&AM$Kc;c35+I(=m3N<NOPB*My0m-pF=8 zB6y~gw)(?6@x{VNX7<Wty9!-zT{fe@W5WLWlFd*4?hL&3Kfs%jNrV}m0Ve-fHv#2< z$z~e%;1pb;VF0FAYnX%Sry90kT2(UuBCqKOrcY`Hf@xVTD=;0U<qW15Xod2k<bcUc f+Cd7~!T}f*V6dc7M}6`H6)9+t$gzD=2Z;j!5!#u3 delta 929 zcmX>Wek7bXz?+$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~=)Ip3w<tvG zBg>T|$5%c1bvP-#Cnh;&qHTeT%0g=gC($4OZnNEA#G=Z5K1SX&`)2!LM%jB7_q06o zKCC}`x4e3TlJ~K#igOMrbtP9Pt(C1&*D&<?p#9{LWqlj-wBNUnKQVN4pQ~@AojBjZ zPkW;CPvI%sgl4Xu^oa4Ak!s)p-)CZc8dFw0oo=(4YlRZ)k{>-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-<E%Gf4)}w#`aB)@v#MKRD%v~U$bJW^xl_s=a;@=tKVxq+t}H} zMKr<2aF4&}N8?Fm>%`B@DX(}miK*VPcCPRBl6+;APib{>bv04@{0n#2WoBQL;A(vJ zLHF^JKeu?6{GRS%|H0aR#oaWID(P1S|Ghjm_%<K&eYdtleu2&}epu?_0HrS5dosI4 z^MNU?5Qr^+*w=OP6csUGT2QFxJ+qOk*+78dg6)KQi-NF4X5A021<AHF{cX%H;%!nZ zQfsQuzh=JYk?%YS<;Wkg1`j>sx|00l=A}4fX54zWaCg|@?0-&+vs90I*61AkzUoqt z1z)CouKbNqX{$+0mU{IjhL5K#{IjM)aJus?gZt(I-i%Bl%<yz?JDEwt7?koSTWHvW zQ*M=p0hnH^VGgFBYuJKmb<F^Xyrv(RKCKxDrscJ)z;uk3Gniha70Qc}@+Y%s2Pt3+ a1YkIT!IDO8^~n=dq=3O;!1h5MBnAMOft4@- diff --git a/Mage.Server/src/main/java/mage/server/Main.java b/Mage.Server/src/main/java/mage/server/Main.java index e8425dbc35..9cec754fa9 100644 --- a/Mage.Server/src/main/java/mage/server/Main.java +++ b/Mage.Server/src/main/java/mage/server/Main.java @@ -35,7 +35,7 @@ import java.io.FilenameFilter; import java.net.InetAddress; import java.util.logging.Level; import java.util.logging.Logger; -import mage.game.GameType; +import mage.game.match.MatchType; import mage.server.game.DeckValidatorFactory; import mage.server.game.GameFactory; import mage.server.game.PlayerFactory; @@ -119,11 +119,11 @@ public class Main { return null; } - private static GameType loadGameType(GamePlugin plugin) { + private static MatchType loadGameType(GamePlugin plugin) { try { classLoader.addURL(new File(pluginFolder + "/" + plugin.getJar()).toURI().toURL()); logger.info("Loading game type: " + plugin.getClassName()); - return (GameType) Class.forName(plugin.getTypeName(), true, classLoader).newInstance(); + return (MatchType) Class.forName(plugin.getTypeName(), true, classLoader).newInstance(); } catch (ClassNotFoundException ex) { logger.log(Level.SEVERE, "Game type not found:" + plugin.getJar() + " - check plugin folder"); } catch (Exception ex) { diff --git a/Mage.Server/src/main/java/mage/server/ServerImpl.java b/Mage.Server/src/main/java/mage/server/ServerImpl.java index 9585c34595..42093e5511 100644 --- a/Mage.Server/src/main/java/mage/server/ServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/ServerImpl.java @@ -43,7 +43,9 @@ import mage.Constants.MultiplayerAttackOption; import mage.Constants.RangeOfInfluence; import mage.cards.decks.DeckCardLists; 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.ClientCallback; @@ -117,9 +119,9 @@ public class ServerImpl extends RemoteServer implements Server { } @Override - public TableView createTable(UUID sessionId, UUID roomId, String gameType, String deckType, List<String> 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 6af3d534bd..e35ee4b9ca 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<String, Class<Game>> games = new HashMap<String, Class<Game>>(); - private Map<String, GameType> gameTypes = new HashMap<String, GameType>(); + private Map<String, Class<Match>> games = new HashMap<String, Class<Match>>(); + private Map<String, MatchType> gameTypes = new HashMap<String, MatchType>(); private List<GameTypeView> gameTypeViews = new ArrayList<GameTypeView>(); @@ -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<Game> con; + Match match; + Constructor<Match> 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<GameTypeView> 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 3e72cdaa06..901a6f12fb 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<TableView> getTables(); public boolean joinTable(UUID sessionId, UUID tableId, String name, DeckCardLists deckList) throws GameException; - public TableView createTable(UUID sessionId, String gameType, String deckType, List<String> 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 a4fc250fa5..f327c23306 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<String> 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 31ef031c3b..48da85e1ee 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<UUID, UUID> sessionPlayerMap = new ConcurrentHashMap<UUID, UUID>(); - public TableController(UUID sessionId, String gameType, String deckType, List<String> 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<UUID, UUID> 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<UUID, UUID> 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 9369d13574..d0ffc29cc4 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<String> 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 b174d9bbf1..6bda8ab0fc 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<String> playerTypes = new ArrayList<String>(); - 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 952c43e4cc..0bf3bc24f8 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 280324324a..40b4ad16f0 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<T extends GameImpl<T>> implements Game, Serializa player = players.getNext(this); } - winnerId = findWinner(); + winnerId = findWinnersAndLosers(); saveState(); } @@ -380,13 +381,21 @@ public abstract class GameImpl<T extends GameImpl<T>> 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 ad58ddd650..0c938da223 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<MatchPlayer> 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 96e6243d11..0dfa4e1d6d 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<T extends Game> implements Match { + protected UUID id = UUID.randomUUID(); protected List<MatchPlayer> players = new ArrayList<MatchPlayer>(); - protected List<Game> games = new ArrayList<Game>(); - protected int winsNeeded; - protected int maxPlayers; - protected int minPlayers; + protected List<T> games = new ArrayList<T>(); + 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 0000000000..5301c55e5b --- /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<String> playerTypes = new ArrayList<String>(); + + 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<String> 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 4402b85c50..cf013ded15 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 4670e3da2e..d3c58fece0 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<T extends MatchType<T>> 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;