diff --git a/.gitattributes b/.gitattributes index 65b64ea97e8..cd2b4ed36f5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -928,15 +928,18 @@ forge-gui-desktop/src/main/java/forge/screens/deckeditor/views/VDeckgen.java -te forge-gui-desktop/src/main/java/forge/screens/deckeditor/views/VProbabilities.java -text forge-gui-desktop/src/main/java/forge/screens/deckeditor/views/VStatistics.java -text forge-gui-desktop/src/main/java/forge/screens/home/CHomeUI.java -text +forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java -text forge-gui-desktop/src/main/java/forge/screens/home/EMenuGroup.java -text forge-gui-desktop/src/main/java/forge/screens/home/EMenuItem.java -text forge-gui-desktop/src/main/java/forge/screens/home/IVSubmenu.java -text forge-gui-desktop/src/main/java/forge/screens/home/LblGroup.java -text forge-gui-desktop/src/main/java/forge/screens/home/LblHeader.java -text forge-gui-desktop/src/main/java/forge/screens/home/LblMenuItem.java -text +forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java -text forge-gui-desktop/src/main/java/forge/screens/home/PnlGroup.java -text forge-gui-desktop/src/main/java/forge/screens/home/StartButton.java -text forge-gui-desktop/src/main/java/forge/screens/home/VHomeUI.java -text +forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java -text forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/CSubmenuGauntletBuild.java -text forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/CSubmenuGauntletContests.java -text forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/CSubmenuGauntletLoad.java -text @@ -947,7 +950,9 @@ forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletBuil forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletContests.java -text forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletLoad.java -text forge-gui-desktop/src/main/java/forge/screens/home/gauntlet/VSubmenuGauntletQuick.java -text +forge-gui-desktop/src/main/java/forge/screens/home/online/COnlineLobby.java -text forge-gui-desktop/src/main/java/forge/screens/home/online/CSubmenuOnlineLobby.java -text +forge-gui-desktop/src/main/java/forge/screens/home/online/VOnlineLobby.java -text forge-gui-desktop/src/main/java/forge/screens/home/online/VSubmenuOnlineLobby.java -text forge-gui-desktop/src/main/java/forge/screens/home/package-info.java -text forge-gui-desktop/src/main/java/forge/screens/home/quest/CSubmenuChallenges.java -text @@ -17425,6 +17430,7 @@ forge-gui/src/main/java/forge/limited/package-info.java svneol=native#text/plain forge-gui/src/main/java/forge/match/AbstractGuiGame.java -text forge-gui/src/main/java/forge/match/HostedMatch.java -text forge-gui/src/main/java/forge/match/MatchConstants.java -text +forge-gui/src/main/java/forge/match/NetGuiGame.java -text forge-gui/src/main/java/forge/match/NextGameDecision.java -text forge-gui/src/main/java/forge/match/input/Input.java -text forge-gui/src/main/java/forge/match/input/InputAttack.java -text @@ -17456,6 +17462,10 @@ forge-gui/src/main/java/forge/model/MetaSet.java -text forge-gui/src/main/java/forge/model/MultipleForgeJarsFoundError.java -text forge-gui/src/main/java/forge/model/UnOpenedMeta.java -text forge-gui/src/main/java/forge/model/package-info.java svneol=native#text/plain +forge-gui/src/main/java/forge/net/FGameClient.java -text +forge-gui/src/main/java/forge/net/FServerManager.java -text +forge-gui/src/main/java/forge/net/NetGame.java -text +forge-gui/src/main/java/forge/net/package-info.java -text forge-gui/src/main/java/forge/planarconquest/ConquestAction.java -text forge-gui/src/main/java/forge/planarconquest/ConquestCommander.java -text forge-gui/src/main/java/forge/planarconquest/ConquestController.java -text @@ -17473,6 +17483,7 @@ forge-gui/src/main/java/forge/player/HumanCostDecision.java -text forge-gui/src/main/java/forge/player/HumanPlay.java -text forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java -text forge-gui/src/main/java/forge/player/LobbyPlayerHuman.java -text +forge-gui/src/main/java/forge/player/NetGameController.java -text forge-gui/src/main/java/forge/player/PlayerControllerHuman.java -text forge-gui/src/main/java/forge/player/TargetSelection.java -text forge-gui/src/main/java/forge/player/package-info.java -text @@ -17587,6 +17598,7 @@ forge-net/src/main/java/forge/net/IConnectionObserver.java -text forge-net/src/main/java/forge/net/Lobby.java -text forge-net/src/main/java/forge/net/LobbyPlayerRemote.java -text forge-net/src/main/java/forge/net/NetServer.java -text +forge-net/src/main/java/forge/net/client/GameServlet.java -text forge-net/src/main/java/forge/net/client/INetClient.java -text forge-net/src/main/java/forge/net/client/InvalidFieldInPacketException.java -text forge-net/src/main/java/forge/net/client/NetClient.java -text @@ -17596,6 +17608,20 @@ forge-net/src/main/java/forge/net/client/state/IClientState.java -text forge-net/src/main/java/forge/net/client/state/InLobbyClientState.java -text forge-net/src/main/java/forge/net/client/state/UnauthorizedClientState.java -text forge-net/src/main/java/forge/net/client/state/package-info.java -text +forge-net/src/main/java/forge/net/game/GuiGameEvent.java -text +forge-net/src/main/java/forge/net/game/IRemote.java -text +forge-net/src/main/java/forge/net/game/LoginEvent.java -text +forge-net/src/main/java/forge/net/game/LogoutEvent.java -text +forge-net/src/main/java/forge/net/game/MessageEvent.java -text +forge-net/src/main/java/forge/net/game/NetEvent.java -text +forge-net/src/main/java/forge/net/game/RegisterDeckEvent.java -text +forge-net/src/main/java/forge/net/game/client/ILobbyListener.java -text +forge-net/src/main/java/forge/net/game/client/IToServer.java -text +forge-net/src/main/java/forge/net/game/client/package-info.java -text +forge-net/src/main/java/forge/net/game/package-info.java -text +forge-net/src/main/java/forge/net/game/server/IToClient.java -text +forge-net/src/main/java/forge/net/game/server/RemoteClient.java -text +forge-net/src/main/java/forge/net/game/server/package-info.java -text forge-net/src/main/java/forge/net/package-info.java -text forge-net/src/main/java/forge/net/protocol/ClientProtocol.java -text forge-net/src/main/java/forge/net/protocol/ClientProtocolJson.java -text diff --git a/forge-gui-desktop/src/main/java/forge/GuiDesktop.java b/forge-gui-desktop/src/main/java/forge/GuiDesktop.java index 69f778ce774..60df3d14339 100644 --- a/forge-gui-desktop/src/main/java/forge/GuiDesktop.java +++ b/forge-gui-desktop/src/main/java/forge/GuiDesktop.java @@ -29,6 +29,7 @@ import forge.download.GuiDownloader; import forge.error.BugReportDialog; import forge.gui.BoxedProductCardListViewer; import forge.gui.CardListViewer; +import forge.gui.FNetOverlay; import forge.gui.GuiChoose; import forge.gui.framework.FScreen; import forge.interfaces.IGuiBase; @@ -278,4 +279,10 @@ public class GuiDesktop implements IGuiBase { Singletons.getControl().addMatch(match); return match; } + + @Override + public void netMessage(final String origin, final String message) { + FNetOverlay.SINGLETON_INSTANCE.showUp(""); + FNetOverlay.SINGLETON_INSTANCE.addMessage(origin, message); + } } \ No newline at end of file diff --git a/forge-gui-desktop/src/main/java/forge/gui/FNetOverlay.java b/forge-gui-desktop/src/main/java/forge/gui/FNetOverlay.java index f1aa9ccbac5..388e50028d3 100644 --- a/forge-gui-desktop/src/main/java/forge/gui/FNetOverlay.java +++ b/forge-gui-desktop/src/main/java/forge/gui/FNetOverlay.java @@ -1,5 +1,9 @@ package forge.gui; +import forge.model.FModel; +import forge.net.FGameClient; +import forge.net.game.MessageEvent; +import forge.properties.ForgePreferences.FPref; import forge.toolbox.*; import forge.toolbox.FSkin.SkinnedPanel; import net.miginfocom.swing.MigLayout; @@ -31,20 +35,26 @@ public enum FNetOverlay { private final FTextField txtInput = new FTextField.Builder().maxLength(60).build(); private final FLabel cmdSend = new FLabel.ButtonBuilder().text("Send").build(); - //private boolean minimized = false; private int height = 120; private int width = 400; - private final ActionListener onSend = new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - String message = txtInput.getText(); - txtInput.setText(""); - if ( StringUtils.isBlank(message) ) - return; + private FGameClient client = null; + public void setGameClient(final FGameClient client) { + this.client = client; + } + private final ActionListener onSend = new ActionListener() { + @Override public final void actionPerformed(final ActionEvent e) { + final String message = txtInput.getText(); + txtInput.setText(""); + if (StringUtils.isBlank(message)) { + return; + } + + if (client != null) { + client.send(new MessageEvent(FModel.getPreferences().getPref(FPref.PLAYER_NAME), message)); + } // lobby.speak(ChatArea.Room, lobby.getGuiPlayer(), message); } }; diff --git a/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java b/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java index 8d16021fc33..1f8ae695a6d 100644 --- a/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java +++ b/forge-gui-desktop/src/main/java/forge/gui/framework/EDocID.java @@ -13,6 +13,7 @@ import forge.screens.home.gauntlet.VSubmenuGauntletBuild; import forge.screens.home.gauntlet.VSubmenuGauntletContests; import forge.screens.home.gauntlet.VSubmenuGauntletLoad; import forge.screens.home.gauntlet.VSubmenuGauntletQuick; +import forge.screens.home.online.VOnlineLobby; import forge.screens.home.online.VSubmenuOnlineLobby; import forge.screens.home.quest.VSubmenuChallenges; import forge.screens.home.quest.VSubmenuDuels; @@ -73,9 +74,11 @@ public enum EDocID { HOME_DRAFT (VSubmenuDraft.SINGLETON_INSTANCE), HOME_SEALED (VSubmenuSealed.SINGLETON_INSTANCE), HOME_WINSTON (VSubmenuWinston.SINGLETON_INSTANCE), - HOME_LOBBY (VSubmenuOnlineLobby.SINGLETON_INSTANCE), + HOME_NETWORK (VSubmenuOnlineLobby.SINGLETON_INSTANCE), HOME_RELEASE_NOTES (VSubmenuReleaseNotes.SINGLETON_INSTANCE), + ONLINE_LOBBY (VOnlineLobby.SINGLETON_INSTANCE), + REPORT_MESSAGE (), REPORT_STACK (), REPORT_COMBAT (), diff --git a/forge-gui-desktop/src/main/java/forge/gui/framework/FScreen.java b/forge-gui-desktop/src/main/java/forge/gui/framework/FScreen.java index 44bcfb4f76f..223619308f6 100644 --- a/forge-gui-desktop/src/main/java/forge/gui/framework/FScreen.java +++ b/forge-gui-desktop/src/main/java/forge/gui/framework/FScreen.java @@ -12,6 +12,8 @@ import forge.screens.deckeditor.CDeckEditorUI; import forge.screens.deckeditor.VDeckEditorUI; import forge.screens.home.CHomeUI; import forge.screens.home.VHomeUI; +import forge.screens.home.online.COnlineLobby; +import forge.screens.home.online.VOnlineLobby; import forge.screens.match.CMatchUI; import forge.screens.match.VMatchUI; import forge.screens.workshop.CWorkshopUI; @@ -151,6 +153,15 @@ public class FScreen { "Leave Bazaar", null, false); + public static final FScreen ONLINE_LOBBY = new FScreen( + VOnlineLobby.SINGLETON_INSTANCE, + COnlineLobby.SINGLETON_INSTANCE, + "Online Lobby", + FSkin.getImage(FSkinProp.IMG_CHAOS), + true, + "Leave Lobby", + null, + false); private final IVTopLevelUI view; private final ICDoc controller; diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/CHomeUI.java b/forge-gui-desktop/src/main/java/forge/screens/home/CHomeUI.java index 2d8644e40aa..28a16274c77 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/CHomeUI.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/CHomeUI.java @@ -1,7 +1,11 @@ package forge.screens.home; -import forge.UiCommand; +import java.util.List; + +import javax.swing.JMenu; + import forge.Singletons; +import forge.UiCommand; import forge.gui.FNetOverlay; import forge.gui.framework.EDocID; import forge.gui.framework.ICDoc; @@ -9,17 +13,12 @@ import forge.menus.IMenuProvider; import forge.menus.MenuUtil; import forge.model.FModel; import forge.net.FServer; -import forge.net.NetServer; +import forge.net.FServerManager; import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; -import forge.properties.ForgeConstants; import forge.screens.home.sanctioned.VSubmenuConstructed; import forge.toolbox.FAbsolutePositioner; -import javax.swing.*; - -import java.util.List; - /** * Assembles Swing components of exit submenu option singleton. * @@ -88,13 +87,13 @@ public enum CHomeUI implements ICDoc, IMenuProvider { VHomeUI.SINGLETON_INSTANCE.getLblStartServer().setCommand(new Runnable() { @Override public void run() { - NetServer srv = FServer.getServer(); - srv.listen(ForgeConstants.SERVER_PORT_NUMBER); + //final NetServer srv = FServer.getServer(); + //srv.listen(ForgeConstants.SERVER_PORT_NUMBER); VHomeUI.SINGLETON_INSTANCE.getLblStopServer().setEnabled(true); VHomeUI.SINGLETON_INSTANCE.getLblStartServer().setEnabled(false); - FNetOverlay.SINGLETON_INSTANCE.showUp("Server listening on port " + srv.getPortNumber()); + FNetOverlay.SINGLETON_INSTANCE.showUp("");//"Server listening on port " + srv.getPortNumber()); } }); @@ -102,6 +101,7 @@ public enum CHomeUI implements ICDoc, IMenuProvider { @Override public void run() { FServer.getServer().stop(); + FServerManager.getInstance().stopServer(); VHomeUI.SINGLETON_INSTANCE.getLblStopServer().setEnabled(false); VHomeUI.SINGLETON_INSTANCE.getLblStartServer().setEnabled(true); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java new file mode 100644 index 00000000000..04140c51225 --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/screens/home/CLobby.java @@ -0,0 +1,372 @@ +package forge.screens.home; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +import javax.swing.SwingUtilities; + +import com.beust.jcommander.internal.Maps; + +import forge.GuiBase; +import forge.LobbyPlayer; +import forge.deck.CardPool; +import forge.deck.Deck; +import forge.deck.DeckFormat; +import forge.deck.DeckSection; +import forge.deck.DeckType; +import forge.deck.DeckgenUtil; +import forge.game.GameType; +import forge.game.player.RegisteredPlayer; +import forge.gui.GuiDialog; +import forge.interfaces.IGuiGame; +import forge.item.PaperCard; +import forge.match.HostedMatch; +import forge.model.CardCollections; +import forge.model.FModel; +import forge.player.GamePlayerUtil; +import forge.properties.ForgePreferences; +import forge.properties.ForgePreferences.FPref; +import forge.toolbox.FList; +import forge.toolbox.FOptionPane; +import forge.util.Aggregates; +import forge.util.storage.IStorage; + +public class CLobby { + + private final VLobby view; + public CLobby(final VLobby view) { + this.view = view; + } + + public void update() { + SwingUtilities.invokeLater(new Runnable() { + @Override public final void run() { + final CardCollections cColl = FModel.getDecks(); + FList deckList; + Vector listData; + Object val; + + for (int i = 0; i < 8; i++) { + // Commander: reinit deck list and restore last selections (if any) + deckList = view.getCommanderDeckLists().get(i); + listData = new Vector(); + listData.add("Generate"); + if (cColl.getCommander().size() > 0) { + listData.add("Random"); + for (Deck comDeck : cColl.getCommander()) { + listData.add(comDeck); + } + } + val = deckList.getSelectedValue(); + deckList.setListData(listData); + if (null != val) { + deckList.setSelectedValue(val, true); + } + if (-1 == deckList.getSelectedIndex()) { + deckList.setSelectedIndex(0); + } // End Commander + + // Archenemy: reinit deck list and restore last selections (if any) + deckList = view.getSchemeDeckLists().get(i); + listData = new Vector(); + listData.add("Use deck's scheme section (random if unavailable)"); + listData.add("Generate"); + if (cColl.getScheme().size() > 0) { + listData.add("Random"); + for (Deck schemeDeck : cColl.getScheme()) { + listData.add(schemeDeck); + } + } + val = deckList.getSelectedValue(); + deckList.setListData(listData); + if (null != val) { + deckList.setSelectedValue(val, true); + } + if (-1 == deckList.getSelectedIndex()) { + deckList.setSelectedIndex(0); + } // End Archenemy + + // Planechase: reinit deck lists and restore last selections (if any) + deckList = view.getPlanarDeckLists().get(i); + listData = new Vector(); + + listData.add("Use deck's planes section (random if unavailable)"); + listData.add("Generate"); + if (cColl.getPlane().size() > 0) { + listData.add("Random"); + for (Deck planarDeck : cColl.getPlane()) { + listData.add(planarDeck); + } + } + + val = deckList.getSelectedValue(); + deckList.setListData(listData); + if (null != val) { + deckList.setSelectedValue(val, true); + } + + if (-1 == deckList.getSelectedIndex()) { + deckList.setSelectedIndex(0); + } // End Planechase + + view.updateVanguardList(i); + } + + // General updates when switching back to this view + view.updatePlayersFromPrefs(); + view.getBtnStart().requestFocusInWindow(); + } + }); + } + + public void initialize() { + view.getDeckChooser(0).initialize(FPref.CONSTRUCTED_P1_DECK_STATE, DeckType.PRECONSTRUCTED_DECK); + view.getDeckChooser(1).initialize(FPref.CONSTRUCTED_P2_DECK_STATE, DeckType.COLOR_DECK); + view.getDeckChooser(2).initialize(FPref.CONSTRUCTED_P3_DECK_STATE, DeckType.COLOR_DECK); + view.getDeckChooser(3).initialize(FPref.CONSTRUCTED_P4_DECK_STATE, DeckType.COLOR_DECK); + view.getDeckChooser(4).initialize(FPref.CONSTRUCTED_P5_DECK_STATE, DeckType.COLOR_DECK); + view.getDeckChooser(5).initialize(FPref.CONSTRUCTED_P6_DECK_STATE, DeckType.COLOR_DECK); + view.getDeckChooser(6).initialize(FPref.CONSTRUCTED_P7_DECK_STATE, DeckType.COLOR_DECK); + view.getDeckChooser(7).initialize(FPref.CONSTRUCTED_P8_DECK_STATE, DeckType.COLOR_DECK); + + // Start button event handling + view.getBtnStart().addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent arg0) { + startGame(view.getAppliedVariants()); + } + }); + + final ForgePreferences prefs = FModel.getPreferences(); + // Checkbox event handling + view.getCbSingletons().addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent arg0) { + prefs.setPref(FPref.DECKGEN_SINGLETONS, String.valueOf(view.getCbSingletons().isSelected())); + prefs.save(); + } + }); + + view.getCbArtifacts().addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent arg0) { + prefs.setPref(FPref.DECKGEN_ARTIFACTS, String.valueOf(view.getCbArtifacts().isSelected())); + prefs.save(); + } + }); + + // Pre-select checkboxes + view.getCbSingletons().setSelected(prefs.getPrefBoolean(FPref.DECKGEN_SINGLETONS)); + view.getCbArtifacts().setSelected(prefs.getPrefBoolean(FPref.DECKGEN_ARTIFACTS)); + } + + /** Starts a match with the applied variants. */ + private void startGame(final Set variantTypes) { + if (!view.isEnoughTeams()) { + FOptionPane.showMessageDialog("There are not enough teams! Please adjust team allocations."); + return; + } + + for (final int i : view.getParticipants()) { + if (view.getDeckChooser(i).getPlayer() == null) { + FOptionPane.showMessageDialog("Please specify a deck for " + view.getPlayerName(i)); + return; + } + } // Is it even possible anymore? I think current implementation assigns decks automatically. + + GameType autoGenerateVariant = null; + boolean isCommanderMatch = false; + boolean isTinyLeadersMatch = false; + if (!variantTypes.isEmpty()) { + isTinyLeadersMatch = variantTypes.contains(GameType.TinyLeaders); + isCommanderMatch = isTinyLeadersMatch || variantTypes.contains(GameType.Commander); + if (!isCommanderMatch) { + for (GameType variant : variantTypes) { + if (variant.isAutoGenerated()) { + autoGenerateVariant = variant; + break; + } + } + } + } + + boolean checkLegality = FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY); + + //Auto-generated decks don't need to be checked here + //Commander deck replaces regular deck and is checked later + if (checkLegality && autoGenerateVariant == null && !isCommanderMatch) { + for (final int i : view.getParticipants()) { + String name = view.getPlayerName(i); + String errMsg = GameType.Constructed.getDeckFormat().getDeckConformanceProblem(view.getDeckChooser(i).getPlayer().getDeck()); + if (null != errMsg) { + FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Deck"); + return; + } + } + } + + final List players = new ArrayList(); + final Map guis = Maps.newHashMap(); + final IGuiGame gui = GuiBase.getInterface().getNewGuiGame(); + for (final int i : view.getParticipants()) { + final String name = view.getPlayerName(i); + final boolean isAI = view.isPlayerAI(i); + final LobbyPlayer lobbyPlayer = isAI + ? GamePlayerUtil.createAiPlayer(name, view.getPlayerAvatar(i), view.getAiOptions(i)) + : GamePlayerUtil.getGuiPlayer(name, i); + RegisteredPlayer rp = view.getDeckChooser(i).getPlayer(); + + if (variantTypes.isEmpty()) { + rp.setTeamNumber(view.getTeam(i)); + players.add(rp.setPlayer(lobbyPlayer)); + } else { + Deck deck = null; + PaperCard vanguardAvatar = null; + if (isCommanderMatch) { + final Object selected = view.getCommanderDeckLists().get(i).getSelectedValue(); + if (selected instanceof String) { + final String sel = (String) selected; + final IStorage comDecks = FModel.getDecks().getCommander(); + if (sel.equals("Random") && comDecks.size() > 0) { + deck = Aggregates.random(comDecks); + } + } else { + deck = (Deck) selected; + } + GameType commanderGameType = isTinyLeadersMatch ? GameType.TinyLeaders : GameType.Commander; + if (deck == null) { //Can be null if player deselects the list selection or chose Generate + deck = DeckgenUtil.generateCommanderDeck(isAI, commanderGameType); + } + if (checkLegality) { + String errMsg = commanderGameType.getDeckFormat().getDeckConformanceProblem(deck); + if (null != errMsg) { + FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid " + commanderGameType + " Deck"); + return; + } + } + } else if (autoGenerateVariant != null) { + deck = autoGenerateVariant.autoGenerateDeck(rp); + CardPool avatarPool = deck.get(DeckSection.Avatar); + if (avatarPool != null) { + vanguardAvatar = avatarPool.get(0); + } + } + + // Initialise variables for other variants + deck = deck == null ? rp.getDeck() : deck; + Iterable schemes = null; + final boolean playerIsArchenemy = view.isPlayerArchenemy(i); + Iterable planes = null; + + //Archenemy + if (variantTypes.contains(GameType.ArchenemyRumble) + || (variantTypes.contains(GameType.Archenemy) && playerIsArchenemy)) { + Object selected = view.getSchemeDeckLists().get(i).getSelectedValue(); + CardPool schemePool = null; + if (selected instanceof String) { + String sel = (String) selected; + if (sel.contains("Use deck's scheme section")) { + if (deck.has(DeckSection.Schemes)) { + schemePool = deck.get(DeckSection.Schemes); + } else { + sel = "Random"; + } + } + IStorage sDecks = FModel.getDecks().getScheme(); + if (sel.equals("Random") && sDecks.size() != 0) { + schemePool = Aggregates.random(sDecks).get(DeckSection.Schemes); + } + } else { + schemePool = ((Deck) selected).get(DeckSection.Schemes); + } + if (schemePool == null) { //Can be null if player deselects the list selection or chose Generate + schemePool = DeckgenUtil.generateSchemePool(); + } + if (checkLegality) { + String errMsg = DeckFormat.getSchemeSectionConformanceProblem(schemePool); + if (null != errMsg) { + FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Scheme Deck"); + return; + } + } + schemes = schemePool.toFlatList(); + } + + //Planechase + if (variantTypes.contains(GameType.Planechase)) { + Object selected = view.getPlanarDeckLists().get(i).getSelectedValue(); + CardPool planePool = null; + if (selected instanceof String) { + String sel = (String) selected; + if (sel.contains("Use deck's planes section")) { + if (deck.has(DeckSection.Planes)) { + planePool = deck.get(DeckSection.Planes); + } else { + sel = "Random"; + } + } + IStorage pDecks = FModel.getDecks().getPlane(); + if (sel.equals("Random") && pDecks.size() != 0) { + planePool = Aggregates.random(pDecks).get(DeckSection.Planes); + } + } else { + planePool = ((Deck) selected).get(DeckSection.Planes); + } + if (planePool == null) { //Can be null if player deselects the list selection or chose Generate + planePool = DeckgenUtil.generatePlanarPool(); + } + if (checkLegality) { + String errMsg = DeckFormat.getPlaneSectionConformanceProblem(planePool); + if (null != errMsg) { + FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Planar Deck"); + return; + } + } + planes = planePool.toFlatList(); + } + + //Vanguard + if (variantTypes.contains(GameType.Vanguard)) { + Object selected = view.getVanguardLists().get(i).getSelectedValue(); + if (selected instanceof String) { + String sel = (String) selected; + if (sel.contains("Use deck's default avatar") && deck.has(DeckSection.Avatar)) { + vanguardAvatar = deck.get(DeckSection.Avatar).get(0); + } else { //Only other string is "Random" + if (isAI) { //AI + vanguardAvatar = Aggregates.random(view.getNonRandomAiAvatars()); + } else { //Human + vanguardAvatar = Aggregates.random(view.getNonRandomHumanAvatars()); + } + } + } else { + vanguardAvatar = (PaperCard)selected; + } + if (vanguardAvatar == null) { //ERROR! null if avatar deselected on list + GuiDialog.message("No Vanguard avatar selected for " + name + + ". Please choose one or disable the Vanguard variant"); + return; + } + } + + rp = RegisteredPlayer.forVariants(variantTypes, deck, schemes, playerIsArchenemy, planes, vanguardAvatar); + rp.setTeamNumber(view.getTeam(i)); + players.add(rp.setPlayer(lobbyPlayer)); + } + + if (!isAI) { + guis.put(rp, gui); + } + view.getDeckChooser(i).saveState(); + } + + final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch(); + hostedMatch.startMatch(GameType.Constructed, variantTypes, players, guis); + } + +} diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/EMenuGroup.java b/forge-gui-desktop/src/main/java/forge/screens/home/EMenuGroup.java index 4fd8f0568de..7bad96f2bd2 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/EMenuGroup.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/EMenuGroup.java @@ -9,7 +9,7 @@ package forge.screens.home; */ public enum EMenuGroup { SANCTIONED ("Sanctioned Formats"), - //ONLINE ("Online Multiplayer"), + ONLINE ("Online Multiplayer"), QUEST ("Quest Mode"), GAUNTLET ("Gauntlets"), SETTINGS ("Game Settings"); diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java b/forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java new file mode 100644 index 00000000000..c3a7f0ae9d0 --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/screens/home/PlayerPanel.java @@ -0,0 +1,670 @@ +package forge.screens.home; + +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.MouseEvent; +import java.util.List; + +import javax.swing.ButtonGroup; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JPopupMenu; + +import net.miginfocom.swing.MigLayout; + +import org.apache.commons.lang3.StringUtils; + +import com.google.common.base.Predicate; + +import forge.Singletons; +import forge.UiCommand; +import forge.assets.FSkinProp; +import forge.deck.DeckSection; +import forge.game.GameType; +import forge.gui.framework.FScreen; +import forge.item.PaperCard; +import forge.model.FModel; +import forge.properties.ForgePreferences; +import forge.properties.ForgePreferences.FPref; +import forge.screens.deckeditor.CDeckEditorUI; +import forge.screens.deckeditor.controllers.CEditorCommander; +import forge.screens.deckeditor.controllers.CEditorVariant; +import forge.screens.home.sanctioned.AvatarSelector; +import forge.toolbox.FComboBox; +import forge.toolbox.FComboBoxWrapper; +import forge.toolbox.FLabel; +import forge.toolbox.FMouseAdapter; +import forge.toolbox.FPanel; +import forge.toolbox.FRadioButton; +import forge.toolbox.FSkin; +import forge.toolbox.FSkin.SkinColor; +import forge.toolbox.FSkin.SkinImage; +import forge.toolbox.FTextField; +import forge.util.MyRandom; +import forge.util.NameGenerator; +import forge.util.gui.SOptionPane; + +@SuppressWarnings("serial") +public class PlayerPanel extends FPanel { + private final static ForgePreferences prefs = FModel.getPreferences(); + private static final SkinColor unfocusedPlayerOverlay = FSkin.getColor(FSkin.Colors.CLR_OVERLAY).alphaColor(120); + + private final int index; + private final boolean allowRemote; + + private final FLabel nameRandomiser; + private final FLabel avatarLabel = new FLabel.Builder().opaque(true).hoverable(true).iconScaleFactor(0.99f).iconInBackground(true).build(); + private int avatarIndex; + + private final FTextField txtPlayerName = new FTextField.Builder().text("Player name").build(); + private FRadioButton radioHuman; + private FRadioButton radioAi; + private JCheckBoxMenuItem radioAiUseSimulation; + private FRadioButton radioOpen; + /** Whether this panel is occupied by a remote player. */ + private boolean isRemote; + + private FComboBoxWrapper teamComboBox = new FComboBoxWrapper(); + private FComboBoxWrapper aeTeamComboBox = new FComboBoxWrapper(); + + private final FLabel deckBtn = new FLabel.ButtonBuilder().text("Select a deck").build(); + private final FLabel deckLabel; + + private final String variantBtnConstraints = "height 30px, hidemode 3"; + + private boolean playerIsArchenemy = false; + private final FLabel scmDeckSelectorBtn = new FLabel.ButtonBuilder().text("Select a scheme deck").build(); + private final FLabel scmDeckEditor = new FLabel.ButtonBuilder().text("Scheme Deck Editor").build(); + private final FLabel scmLabel; + + private final FLabel cmdDeckSelectorBtn = new FLabel.ButtonBuilder().text("Select a Commander deck").build(); + private final FLabel cmdDeckEditor = new FLabel.ButtonBuilder().text("Commander Deck Editor").build(); + private final FLabel cmdLabel; + + private final FLabel pchDeckSelectorBtn = new FLabel.ButtonBuilder().text("Select a planar deck").build(); + private final FLabel pchDeckEditor = new FLabel.ButtonBuilder().text("Planar Deck Editor").build(); + private final FLabel pchLabel; + + private final FLabel vgdSelectorBtn = new FLabel.ButtonBuilder().text("Select a Vanguard avatar").build(); + private final FLabel vgdLabel; + + private final VLobby lobby; + public PlayerPanel(final VLobby lobby, final int index, final boolean allowRemote) { + super(); + this.lobby = lobby; + this.deckLabel = lobby.newLabel("Deck:"); + this.scmLabel = lobby.newLabel("Scheme deck:"); + this.cmdLabel = lobby.newLabel("Commander deck:"); + this.pchLabel = lobby.newLabel("Planar deck:"); + this.vgdLabel = lobby.newLabel("Vanguard:"); + + this.index = index; + this.allowRemote = allowRemote; + this.playerIsArchenemy = index == 0; + + setLayout(new MigLayout("insets 10px, gap 5px")); + + // Add a button to players 3+ (or if server) to remove them from the setup + if (index >= 2 || allowRemote) { + FLabel closeBtn = createCloseButton(); + this.add(closeBtn, "w 20, h 20, pos (container.w-20) 0"); + } + + createAvatar(); + this.add(avatarLabel, "spany 2, width 80px, height 80px"); + + createNameEditor(); + this.add(lobby.newLabel("Name:"), "w 40px, h 30px, gaptop 5px"); + this.add(txtPlayerName, "h 30px, pushx, growx"); + + nameRandomiser = createNameRandomizer(); + this.add(nameRandomiser, "h 30px, w 30px, gaptop 5px"); + + createPlayerTypeOptions(); + this.add(radioHuman, "gapright 5px"); + this.add(radioAi, "wrap"); + + this.add(lobby.newLabel("Team:"), "w 40px, h 30px"); + populateTeamsComboBoxes(); + teamComboBox.addActionListener(teamListener); + aeTeamComboBox.addActionListener(teamListener); + teamComboBox.addTo(this, variantBtnConstraints + ", pushx, growx, gaptop 5px"); + aeTeamComboBox.addTo(this, variantBtnConstraints + ", pushx, growx, gaptop 5px"); + if (allowRemote) { + this.add(radioOpen, "gapleft 1px"); + } + + this.add(deckLabel, variantBtnConstraints + ", cell 0 2, sx 2, ax right"); + this.add(deckBtn, variantBtnConstraints + ", cell 2 2, pushx, growx, wmax 100%-153px, h 30px, spanx 4, wrap"); + + addHandlersDeckSelector(); + + this.add(cmdLabel, variantBtnConstraints + ", cell 0 3, sx 2, ax right"); + this.add(cmdDeckSelectorBtn, variantBtnConstraints + ", cell 2 3, growx, pushx"); + this.add(cmdDeckEditor, variantBtnConstraints + ", cell 3 3, sx 3, growx, wrap"); + + this.add(scmLabel, variantBtnConstraints + ", cell 0 4, sx 2, ax right"); + this.add(scmDeckSelectorBtn, variantBtnConstraints + ", cell 2 4, growx, pushx"); + this.add(scmDeckEditor, variantBtnConstraints + ", cell 3 4, sx 3, growx, wrap"); + + this.add(pchLabel, variantBtnConstraints + ", cell 0 5, sx 2, ax right"); + this.add(pchDeckSelectorBtn, variantBtnConstraints + ", cell 2 5, growx, pushx"); + this.add(pchDeckEditor, variantBtnConstraints + ", cell 3 5, sx 3, growx, wrap"); + + this.add(vgdLabel, variantBtnConstraints + ", cell 0 6, sx 2, ax right"); + this.add(vgdSelectorBtn, variantBtnConstraints + ", cell 2 6, sx 4, growx, wrap"); + + addHandlersToVariantsControls(); + updateVariantControlsVisibility(); + + this.addMouseListener(new FMouseAdapter() { + @Override public final void onLeftMouseDown(final MouseEvent e) { + avatarLabel.requestFocusInWindow(); + } + }); + + update(); + } + + private void update() { + final boolean enableComponents = !(isOpen() || isRemote()); + avatarLabel.setEnabled(enableComponents); + txtPlayerName.setEnabled(enableComponents); + nameRandomiser.setEnabled(enableComponents); + deckLabel.setVisible(enableComponents); + deckBtn.setVisible(enableComponents); + } + + private final FMouseAdapter radioMouseAdapter = new FMouseAdapter() { + @Override public final void onLeftClick(final MouseEvent e) { + avatarLabel.requestFocusInWindow(); + lobby.updateVanguardList(index); + update(); + } + }; + + /** Listens to name text fields and gives the appropriate player focus. + * Also saves the name preference when leaving player one's text field. */ + private FocusAdapter nameFocusListener = new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + lobby.changePlayerFocus(index); + } + + @Override + public void focusLost(FocusEvent e) { + final Object source = e.getSource(); + if (source instanceof FTextField) { // the text box + FTextField nField = (FTextField)source; + String newName = nField.getText().trim(); + if (index == 0 && !StringUtils.isBlank(newName) + && StringUtils.isAlphanumericSpace(newName) && prefs.getPref(FPref.PLAYER_NAME) != newName) { + prefs.setPref(FPref.PLAYER_NAME, newName); + prefs.save(); + } + } + } + }; + + /** Listens to avatar buttons and gives the appropriate player focus. */ + private FocusAdapter avatarFocusListener = new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + lobby.changePlayerFocus(index); + } + }; + + private FMouseAdapter avatarMouseListener = new FMouseAdapter() { + @Override public final void onLeftClick(final MouseEvent e) { + if (!avatarLabel.isEnabled()) { + return; + } + + final FLabel avatar = (FLabel)e.getSource(); + + PlayerPanel.this.lobby.changePlayerFocus(index); + avatar.requestFocusInWindow(); + + final AvatarSelector aSel = new AvatarSelector(getPlayerName(), avatarIndex, PlayerPanel.this.lobby.getUsedAvatars()); + for (final FLabel lbl : aSel.getSelectables()) { + lbl.setCommand(new UiCommand() { + @Override + public void run() { + setAvatar(Integer.valueOf(lbl.getName().substring(11))); + aSel.setVisible(false); + } + }); + } + + aSel.setVisible(true); + aSel.dispose(); + + if (index < 2) { + PlayerPanel.this.lobby.updateAvatarPrefs(); + } + } + @Override public final void onRightClick(final MouseEvent e) { + if (!avatarLabel.isEnabled()) { + return; + } + + PlayerPanel.this.lobby.changePlayerFocus(index); + avatarLabel.requestFocusInWindow(); + + setRandomAvatar(); + + if (index < 2) { + PlayerPanel.this.lobby.updateAvatarPrefs(); + } + } + }; + + public void updateVariantControlsVisibility() { + boolean isCommanderApplied = false; + boolean isPlanechaseApplied = false; + boolean isVanguardApplied = false; + boolean isArchenemyApplied = false; + boolean archenemyVisiblity = false; + boolean isDeckBuildingAllowed = true; + + for (final GameType variant : lobby.getAppliedVariants()) { + switch (variant) { + case Archenemy: + isArchenemyApplied = true; + if (playerIsArchenemy) { + archenemyVisiblity = true; + } + break; + case ArchenemyRumble: + archenemyVisiblity = true; + break; + case Commander: + case TinyLeaders: + isCommanderApplied = true; + isDeckBuildingAllowed = false; //Commander deck replaces basic deck, so hide that + break; + case Planechase: + isPlanechaseApplied = true; + break; + case Vanguard: + isVanguardApplied = true; + break; + default: + if (variant.isAutoGenerated()) { + isDeckBuildingAllowed = false; + } + break; + } + } + + deckLabel.setVisible(isDeckBuildingAllowed); + deckBtn.setVisible(isDeckBuildingAllowed); + cmdDeckSelectorBtn.setVisible(isCommanderApplied); + cmdDeckEditor.setVisible(isCommanderApplied); + cmdLabel.setVisible(isCommanderApplied); + + scmDeckSelectorBtn.setVisible(archenemyVisiblity); + scmDeckEditor.setVisible(archenemyVisiblity); + scmLabel.setVisible(archenemyVisiblity); + + teamComboBox.setVisible(!isArchenemyApplied); + aeTeamComboBox.setVisible(isArchenemyApplied); + aeTeamComboBox.setEnabled(!(isArchenemyApplied && playerIsArchenemy)); + + pchDeckSelectorBtn.setVisible(isPlanechaseApplied); + pchDeckEditor.setVisible(isPlanechaseApplied); + pchLabel.setVisible(isPlanechaseApplied); + + vgdSelectorBtn.setVisible(isVanguardApplied); + vgdLabel.setVisible(isVanguardApplied); + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + if (lobby.getPlayerPanelWithFocus() != this) { + FSkin.setGraphicsColor(g, unfocusedPlayerOverlay); + g.fillRect(0, 0, this.getWidth(), this.getHeight()); + } + } + + int getIndex() { + return index; + } + + public boolean isAi() { + return radioAi.isSelected(); + } + + public boolean isSimulatedAi() { + return radioAi.isSelected() && radioAiUseSimulation.isSelected(); + } + + public boolean isOpen() { + return radioOpen.isSelected() && !isRemote; + } + + public boolean isArchenemy() { + return playerIsArchenemy; + } + + public boolean isRemote() { + return isRemote; + } + + public void setRemote(final boolean remote) { + isRemote = remote; + update(); + } + + public void setVanguardButtonText(String text) { + vgdSelectorBtn.setText(text); + } + + public void setDeckSelectorButtonText(String text) { + deckBtn.setText(text); + } + + public void focusOnAvatar() { + avatarLabel.requestFocusInWindow(); + } + + private void populateTeamsComboBoxes() { + aeTeamComboBox.addItem("Archenemy"); + aeTeamComboBox.addItem("Heroes"); + aeTeamComboBox.setSelectedIndex(lobby.getArchenemyTeams().get(index) - 1); + aeTeamComboBox.setEnabled(playerIsArchenemy); + + for (int i = 1; i <= VLobby.MAX_PLAYERS; i++) { + teamComboBox.addItem(i); + } + teamComboBox.setSelectedIndex(lobby.getTeams().get(index) - 1); + teamComboBox.setEnabled(true); + } + + private ActionListener teamListener = new ActionListener() { + @SuppressWarnings("unchecked") + @Override + public void actionPerformed(ActionEvent e) { + FComboBox cb = (FComboBox)e.getSource(); + cb.requestFocusInWindow(); + Object selection = cb.getSelectedItem(); + + if (null == selection) { + return; + } + if (PlayerPanel.this.lobby.getAppliedVariants().contains(GameType.Archenemy)) { + String sel = (String) selection; + if (sel.contains("Archenemy")) { + PlayerPanel.this.lobby.setLastArchenemy(index); + for (PlayerPanel pp : PlayerPanel.this.lobby.getPlayerPanels()) { + int i = pp.index; + PlayerPanel.this.lobby.getArchenemyTeams().set(i, i == PlayerPanel.this.lobby.getLastArchenemy() ? 1 : 2); + pp.aeTeamComboBox.setSelectedIndex(i == PlayerPanel.this.lobby.getLastArchenemy() ? 0 : 1); + pp.toggleIsPlayerArchenemy(); + } + } + } else { + Integer sel = (Integer) selection; + PlayerPanel.this.lobby.getTeams().set(index, sel); + } + + PlayerPanel.this.lobby.changePlayerFocus(index); + } + }; + + public void toggleIsPlayerArchenemy() { + if (lobby.getAppliedVariants().contains(GameType.Archenemy)) { + playerIsArchenemy = lobby.getLastArchenemy() == index; + } else { + playerIsArchenemy = lobby.getAppliedVariants().contains(GameType.ArchenemyRumble); + } + updateVariantControlsVisibility(); + } + + /** + * @param index + */ + private void addHandlersToVariantsControls() { + // Archenemy buttons + scmDeckSelectorBtn.setCommand(new Runnable() { + @Override + public void run() { + PlayerPanel.this.lobby.setCurrentGameMode(PlayerPanel.this.lobby.getVntArchenemy().isSelected() ? GameType.Archenemy : GameType.ArchenemyRumble); + scmDeckSelectorBtn.requestFocusInWindow(); + PlayerPanel.this.lobby.changePlayerFocus(index, PlayerPanel.this.lobby.getCurrentGameMode()); + } + }); + + scmDeckEditor.setCommand(new UiCommand() { + @Override + public void run() { + PlayerPanel.this.lobby.setCurrentGameMode(PlayerPanel.this.lobby.getVntArchenemy().isSelected() ? GameType.Archenemy : GameType.ArchenemyRumble); + Predicate predSchemes = new Predicate() { + @Override + public boolean apply(PaperCard arg0) { + return arg0.getRules().getType().isScheme(); + } + }; + + Singletons.getControl().setCurrentScreen(FScreen.DECK_EDITOR_ARCHENEMY); + CDeckEditorUI.SINGLETON_INSTANCE.setEditorController( + new CEditorVariant(FModel.getDecks().getScheme(), predSchemes, DeckSection.Schemes, FScreen.DECK_EDITOR_ARCHENEMY, CDeckEditorUI.SINGLETON_INSTANCE.getCDetailPicture())); + } + }); + + // Commander buttons + cmdDeckSelectorBtn.setCommand(new Runnable() { + @Override + public void run() { + PlayerPanel.this.lobby.setCurrentGameMode(PlayerPanel.this.lobby.getVntTinyLeaders().isSelected() ? GameType.TinyLeaders : GameType.Commander); + cmdDeckSelectorBtn.requestFocusInWindow(); + PlayerPanel.this.lobby.changePlayerFocus(index, PlayerPanel.this.lobby.getCurrentGameMode()); + } + }); + + cmdDeckEditor.setCommand(new UiCommand() { + @Override + public void run() { + PlayerPanel.this.lobby.setCurrentGameMode(PlayerPanel.this.lobby.getVntTinyLeaders().isSelected() ? GameType.TinyLeaders : GameType.Commander); + Singletons.getControl().setCurrentScreen(FScreen.DECK_EDITOR_COMMANDER); + CDeckEditorUI.SINGLETON_INSTANCE.setEditorController(new CEditorCommander(CDeckEditorUI.SINGLETON_INSTANCE.getCDetailPicture())); + } + }); + + // Planechase buttons + pchDeckSelectorBtn.setCommand(new Runnable() { + @Override + public void run() { + PlayerPanel.this.lobby.setCurrentGameMode(GameType.Planechase); + pchDeckSelectorBtn.requestFocusInWindow(); + PlayerPanel.this.lobby.changePlayerFocus(index, GameType.Planechase); + } + }); + + pchDeckEditor.setCommand(new UiCommand() { + @Override + public void run() { + PlayerPanel.this.lobby.setCurrentGameMode(GameType.Planechase); + Predicate predPlanes = new Predicate() { + @Override + public boolean apply(PaperCard arg0) { + return arg0.getRules().getType().isPlane() || arg0.getRules().getType().isPhenomenon(); + } + }; + + Singletons.getControl().setCurrentScreen(FScreen.DECK_EDITOR_PLANECHASE); + CDeckEditorUI.SINGLETON_INSTANCE.setEditorController( + new CEditorVariant(FModel.getDecks().getPlane(), predPlanes, DeckSection.Planes, FScreen.DECK_EDITOR_PLANECHASE, CDeckEditorUI.SINGLETON_INSTANCE.getCDetailPicture())); + } + }); + + // Vanguard buttons + vgdSelectorBtn.setCommand(new Runnable() { + @Override + public void run() { + PlayerPanel.this.lobby.setCurrentGameMode(GameType.Vanguard); + vgdSelectorBtn.requestFocusInWindow(); + PlayerPanel.this.lobby.changePlayerFocus(index, GameType.Vanguard); + } + }); + } + + /** + * @param index + */ + private void createPlayerTypeOptions() { + radioHuman = new FRadioButton(allowRemote ? "Local" : "Human", index == 0); + radioAi = new FRadioButton("AI", !allowRemote && index != 0); + radioOpen = new FRadioButton("Open", allowRemote && index != 0); + final JPopupMenu menu = new JPopupMenu(); + radioAiUseSimulation = new JCheckBoxMenuItem("Use Simulation"); + menu.add(radioAiUseSimulation); + radioAi.setComponentPopupMenu(menu); + + radioHuman.addMouseListener(radioMouseAdapter); + radioAi.addMouseListener(radioMouseAdapter); + radioOpen.addMouseListener(radioMouseAdapter); + + final ButtonGroup tempBtnGroup = new ButtonGroup(); + tempBtnGroup.add(radioHuman); + tempBtnGroup.add(radioAi); + tempBtnGroup.add(radioOpen); + } + + /** + * @param index + */ + private void addHandlersDeckSelector() { + deckBtn.setCommand(new Runnable() { + @Override + public void run() { + PlayerPanel.this.lobby.setCurrentGameMode(GameType.Constructed); + deckBtn.requestFocusInWindow(); + PlayerPanel.this.lobby.changePlayerFocus(index, GameType.Constructed); + } + }); + } + + /** + * @param index + * @return + */ + private FLabel createNameRandomizer() { + final FLabel newNameBtn = new FLabel.Builder().tooltip("Get a new random name").iconInBackground(false) + .icon(FSkin.getIcon(FSkinProp.ICO_EDIT)).hoverable(true).opaque(false) + .unhoveredAlpha(0.9f).build(); + newNameBtn.setCommand(new UiCommand() { + @Override + public void run() { + String newName = PlayerPanel.this.lobby.getNewName(); + if (null == newName) { + return; + } + txtPlayerName.setText(newName); + + if (index == 0) { + prefs.setPref(FPref.PLAYER_NAME, newName); + prefs.save(); + } + txtPlayerName.requestFocus(); + PlayerPanel.this.lobby.changePlayerFocus(index); + } + }); + newNameBtn.addFocusListener(nameFocusListener); + return newNameBtn; + } + + /** + * @param index + * @return + */ + private void createNameEditor() { + String name; + if (index == 0) { + name = FModel.getPreferences().getPref(FPref.PLAYER_NAME); + if (name.isEmpty()) { + name = "Human"; + } + } + else { + name = NameGenerator.getRandomName("Any", "Any", lobby.getPlayerNames()); + } + + txtPlayerName.setText(name); + txtPlayerName.setFocusable(true); + txtPlayerName.setFont(FSkin.getFont(14)); + txtPlayerName.addActionListener(lobby.nameListener); + txtPlayerName.addFocusListener(nameFocusListener); + } + + private FLabel createCloseButton() { + final FLabel closeBtn = new FLabel.Builder().tooltip("Remove").iconInBackground(false) + .icon(FSkin.getIcon(FSkinProp.ICO_CLOSE)).hoverable(true).build(); + closeBtn.setCommand(new Runnable() { + @Override public final void run() { + if (isRemote() && !SOptionPane.showConfirmDialog("Really kick player?", "Kick", false)) { + return; + } + PlayerPanel.this.lobby.removePlayer(index); + } + }); + return closeBtn; + } + + private void createAvatar() { + String[] currentPrefs = FModel.getPreferences().getPref(FPref.UI_AVATARS).split(","); + if (index < currentPrefs.length) { + avatarIndex = Integer.parseInt(currentPrefs[index]); + avatarLabel.setIcon(FSkin.getAvatars().get(avatarIndex)); + } + else { + setRandomAvatar(); + } + + avatarLabel.setToolTipText("L-click: Select avatar. R-click: Randomize avatar."); + avatarLabel.addFocusListener(avatarFocusListener); + avatarLabel.addMouseListener(avatarMouseListener); + } + + /** Applies a random avatar, avoiding avatars already used. + * @param playerIndex */ + public void setRandomAvatar() { + int random = 0; + + List usedAvatars = lobby.getUsedAvatars(); + do { + random = MyRandom.getRandom().nextInt(FSkin.getAvatars().size()); + } while (usedAvatars.contains(random)); + setAvatar(random); + } + + public void setAvatar(int newAvatarIndex) { + avatarIndex = newAvatarIndex; + SkinImage icon = FSkin.getAvatars().get(newAvatarIndex); + avatarLabel.setIcon(icon); + avatarLabel.repaintSelf(); + } + + private final FSkin.LineSkinBorder focusedBorder = new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_BORDERS).alphaColor(255), 3); + private final FSkin.LineSkinBorder defaultBorder = new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_THEME).alphaColor(200), 2); + + public void setFocused(boolean focused) { + avatarLabel.setBorder(focused ? focusedBorder : defaultBorder); + avatarLabel.setHoverable(focused); + } + + public int getAvatarIndex() { + return avatarIndex; + } + + public void setPlayerName(String string) { + txtPlayerName.setText(string); + } + + public String getPlayerName() { + return txtPlayerName.getText(); + } +} \ No newline at end of file diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/VHomeUI.java b/forge-gui-desktop/src/main/java/forge/screens/home/VHomeUI.java index 6a2b3cb8933..5e83e8300fa 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/VHomeUI.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/VHomeUI.java @@ -17,17 +17,43 @@ */ package forge.screens.home; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingConstants; + +import net.miginfocom.swing.MigLayout; import forge.Singletons; import forge.assets.FSkinProp; -import forge.gui.framework.*; +import forge.gui.framework.EDocID; +import forge.gui.framework.FScreen; +import forge.gui.framework.ICDoc; +import forge.gui.framework.ILocalRepaint; +import forge.gui.framework.IVTopLevelUI; import forge.model.FModel; -import forge.properties.ForgePreferences.FPref; import forge.properties.ForgeConstants; +import forge.properties.ForgePreferences.FPref; import forge.screens.home.gauntlet.VSubmenuGauntletBuild; import forge.screens.home.gauntlet.VSubmenuGauntletContests; import forge.screens.home.gauntlet.VSubmenuGauntletLoad; import forge.screens.home.gauntlet.VSubmenuGauntletQuick; -import forge.screens.home.quest.*; +import forge.screens.home.online.VSubmenuOnlineLobby; +import forge.screens.home.quest.VSubmenuChallenges; +import forge.screens.home.quest.VSubmenuDuels; +import forge.screens.home.quest.VSubmenuQuestData; +import forge.screens.home.quest.VSubmenuQuestDecks; +import forge.screens.home.quest.VSubmenuQuestDraft; +import forge.screens.home.quest.VSubmenuQuestPrefs; import forge.screens.home.sanctioned.VSubmenuConstructed; import forge.screens.home.sanctioned.VSubmenuDraft; import forge.screens.home.sanctioned.VSubmenuSealed; @@ -42,13 +68,6 @@ import forge.toolbox.FSkin; import forge.toolbox.FSkin.SkinColor; import forge.toolbox.FSkin.SkinnedPanel; import forge.view.FView; -import net.miginfocom.swing.MigLayout; - -import javax.swing.*; - -import java.awt.*; -import java.util.*; -import java.util.List; /** * Top level view class for home UI drag layout.
diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java new file mode 100644 index 00000000000..fdaf0b2c88a --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java @@ -0,0 +1,792 @@ +package forge.screens.home; + +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.ScrollPaneConstants; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.miginfocom.swing.MigLayout; + +import org.apache.commons.lang3.StringUtils; + +import forge.UiCommand; +import forge.deck.DeckProxy; +import forge.deck.DeckType; +import forge.deckchooser.DecksComboBoxEvent; +import forge.deckchooser.FDeckChooser; +import forge.deckchooser.IDecksComboBoxListener; +import forge.game.GameType; +import forge.game.card.CardView; +import forge.gui.CardDetailPanel; +import forge.item.PaperCard; +import forge.model.FModel; +import forge.properties.ForgePreferences; +import forge.properties.ForgePreferences.FPref; +import forge.toolbox.FCheckBox; +import forge.toolbox.FLabel; +import forge.toolbox.FList; +import forge.toolbox.FOptionPane; +import forge.toolbox.FPanel; +import forge.toolbox.FScrollPane; +import forge.toolbox.FScrollPanel; +import forge.toolbox.FSkin; +import forge.toolbox.FSkin.SkinImage; +import forge.toolbox.FTextField; +import forge.util.Lang; +import forge.util.NameGenerator; + +/** + * Lobby view. View of a number of players at the deck selection stage. + * + *

(V at beginning of class name denotes a view class.) + */ +public class VLobby { + + static final int MAX_PLAYERS = 8; + private static final ForgePreferences prefs = FModel.getPreferences(); + + // General variables + private final boolean allowRemote; + private final LblHeader lblTitle = new LblHeader("Sanctioned Format: Constructed"); + private int activePlayersNum = 2; + private int playerWithFocus = 0; // index of the player that currently has focus + private PlayerPanel playerPanelWithFocus; + private GameType currentGameMode = GameType.Constructed; + private List teams = new ArrayList(MAX_PLAYERS); + private List archenemyTeams = new ArrayList(MAX_PLAYERS); + + private final StartButton btnStart = new StartButton(); + private final JPanel pnlStart = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); + private final JPanel constructedFrame = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); // Main content frame + + // Variants frame and variables + private final Set appliedVariants = new TreeSet(); + private final FPanel variantsPanel = new FPanel(new MigLayout("insets 10, gapx 10")); + private final VariantCheckBox vntVanguard = new VariantCheckBox(GameType.Vanguard); + private final VariantCheckBox vntMomirBasic = new VariantCheckBox(GameType.MomirBasic); + private final VariantCheckBox vntCommander = new VariantCheckBox(GameType.Commander); + private final VariantCheckBox vntTinyLeaders = new VariantCheckBox(GameType.TinyLeaders); + private final VariantCheckBox vntPlanechase = new VariantCheckBox(GameType.Planechase); + private final VariantCheckBox vntArchenemy = new VariantCheckBox(GameType.Archenemy); + private final VariantCheckBox vntArchenemyRumble = new VariantCheckBox(GameType.ArchenemyRumble); + + // Player frame elements + private final JPanel playersFrame = new JPanel(new MigLayout("insets 0, gap 0 5, wrap, hidemode 3")); + private final FScrollPanel playersScroll = new FScrollPanel(new MigLayout("insets 0, gap 0, wrap, hidemode 3"), true); + private final List playerPanels = new ArrayList(MAX_PLAYERS); + + private final FLabel addPlayerBtn = new FLabel.ButtonBuilder().fontSize(14).text("Add a Player").build(); + + // Deck frame elements + private final JPanel decksFrame = new JPanel(new MigLayout("insets 0, gap 0, wrap, hidemode 3")); + private final List deckChoosers = new ArrayList(8); + private final FCheckBox cbSingletons = new FCheckBox("Singleton Mode"); + private final FCheckBox cbArtifacts = new FCheckBox("Remove Artifacts"); + + // Variants + private final List> schemeDeckLists = new ArrayList>(); + private final List schemeDeckPanels = new ArrayList(MAX_PLAYERS); + private int lastArchenemy = 0; + + private final List> commanderDeckLists = new ArrayList>(); + private final List commanderDeckPanels = new ArrayList(MAX_PLAYERS); + + private final List> planarDeckLists = new ArrayList>(); + private final List planarDeckPanels = new ArrayList(MAX_PLAYERS); + + private final List> vgdAvatarLists = new ArrayList>(); + private final List vgdPanels = new ArrayList(MAX_PLAYERS); + private final List vgdAvatarDetails = new ArrayList(); + private final List vgdAllAvatars = new ArrayList(); + private final List vgdAllAiAvatars = new ArrayList(); + private final List nonRandomHumanAvatars = new ArrayList(); + private final List nonRandomAiAvatars = new ArrayList(); + private final Vector humanListData = new Vector(); + private final Vector aiListData = new Vector(); + + // CTR + public VLobby(final boolean allowRemote) { + this.allowRemote = allowRemote; + lblTitle.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2)); + + //////////////////////////////////////////////////////// + //////////////////// Variants Panel //////////////////// + + variantsPanel.setOpaque(false); + variantsPanel.add(newLabel("Variants:")); + variantsPanel.add(vntVanguard); + variantsPanel.add(vntMomirBasic); + variantsPanel.add(vntCommander); + variantsPanel.add(vntTinyLeaders); + variantsPanel.add(vntPlanechase); + variantsPanel.add(vntArchenemy); + variantsPanel.add(vntArchenemyRumble); + + constructedFrame.add(new FScrollPane(variantsPanel, false, true, + ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), + "w 100%, h 45px!, gapbottom 10px, spanx 2, wrap"); + + //////////////////////////////////////////////////////// + ///////////////////// Player Panel ///////////////////// + + // Construct individual player panels + String constraints = "pushx, growx, wrap, hidemode 3"; + for (int i = 0; i < MAX_PLAYERS; i++) { + teams.add(i + 1); + archenemyTeams.add(i == 0 ? 1 : 2); + + final PlayerPanel player = new PlayerPanel(this, i, allowRemote); + playerPanels.add(player); + + // Populate players panel + player.setVisible(i < activePlayersNum); + + playersScroll.add(player, constraints); + + if (i == 0) { + constraints += ", gaptop 5px"; + } + } + + playerPanelWithFocus = playerPanels.get(0); + playerPanelWithFocus.setFocused(true); + + playersFrame.setOpaque(false); + playersFrame.add(playersScroll, "w 100%, h 100%-35px"); + + addPlayerBtn.setFocusable(true); + addPlayerBtn.setCommand(new Runnable() { + @Override + public void run() { + addPlayer(); + } + }); + playersFrame.add(addPlayerBtn, "height 30px!, growx, pushx"); + + constructedFrame.add(playersFrame, "gapright 10px, w 50%-5px, growy, pushy"); + + //////////////////////////////////////////////////////// + ////////////////////// Deck Panel ////////////////////// + + for (int i = 0; i < MAX_PLAYERS; i++) { + buildDeckPanel(i); + } + constructedFrame.add(decksFrame, "w 50%-5px, growy, pushy"); + constructedFrame.setOpaque(false); + decksFrame.setOpaque(false); + + // Start Button + pnlStart.setOpaque(false); + pnlStart.add(btnStart, "align center"); + } + + public void populate() { + for (final FDeckChooser fdc : deckChoosers) { + fdc.populate(); + fdc.getDecksComboBox().addListener(new IDecksComboBoxListener() { + @Override + public void deckTypeSelected(DecksComboBoxEvent ev) { + playerPanelWithFocus.focusOnAvatar(); + } + }); + } + populateDeckPanel(GameType.Constructed); + populateVanguardLists(); + + } + + public void addPlayerInFreeSlot(final String name) { + if (activePlayersNum >= MAX_PLAYERS) { + return; + } + + for (final PlayerPanel pp : getPlayerPanels()) { + if (pp.isVisible() && pp.isOpen()) { + addPlayer(pp.getIndex()); + pp.setPlayerName(name); + pp.setRemote(true); + + return; + } + } + } + private void addPlayer() { + if (activePlayersNum >= MAX_PLAYERS) { + return; + } + + int freeIndex = -1; + for (int i = 0; i < MAX_PLAYERS; i++) { + if (!playerPanels.get(i).isVisible()) { + freeIndex = i; + break; + } + } + addPlayer(freeIndex); + } + private void addPlayer(final int slot) { + playerPanels.get(slot).setVisible(true); + + activePlayersNum++; + addPlayerBtn.setEnabled(activePlayersNum < MAX_PLAYERS); + + playerPanels.get(slot).setVisible(true); + playerPanels.get(slot).focusOnAvatar(); + } + + void removePlayer(final int playerIndex) { + if (activePlayersNum < playerIndex) { + return; + } + activePlayersNum--; + final FPanel player = playerPanels.get(playerIndex); + player.setVisible(false); + addPlayerBtn.setEnabled(true); + + //find closest player still in game and give focus + int min = MAX_PLAYERS; + final List participants = getParticipants(); + if (!participants.isEmpty()) { + int closest = 2; + + for (final int participantIndex : getParticipants()) { + final int diff = Math.abs(playerIndex - participantIndex); + + if (diff < min) { + min = diff; + closest = participantIndex; + } + } + + changePlayerFocus(closest); + playerPanels.get(closest).focusOnAvatar(); + } + } + + /** Builds the actual deck panel layouts for each player. + * These are added to a list which can be referenced to populate the deck panel appropriately. */ + @SuppressWarnings("serial") + private void buildDeckPanel(final int playerIndex) { + String sectionConstraints = "insets 0, gap 0, wrap"; + String labelConstraints = "gaptop 10px, gapbottom 5px"; + + // Main deck + final FDeckChooser mainChooser = new FDeckChooser(null, isPlayerAI(playerIndex)); + mainChooser.initialize(); + mainChooser.getLstDecks().setSelectCommand(new UiCommand() { + @Override + public void run() { + VLobby.this.onDeckClicked(playerIndex, mainChooser.getSelectedDeckType(), mainChooser.getLstDecks().getSelectedItems()); + } + }); + deckChoosers.add(mainChooser); + + // Scheme deck list + FPanel schemeDeckPanel = new FPanel(); + schemeDeckPanel.setBorderToggle(false); + schemeDeckPanel.setLayout(new MigLayout(sectionConstraints)); + schemeDeckPanel.add(new FLabel.Builder().text("Select Scheme deck:").build(), labelConstraints); + FList schemeDeckList = new FList(); + schemeDeckList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + + FScrollPane scrSchemes = new FScrollPane(schemeDeckList, true, + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + schemeDeckPanel.add(scrSchemes, "grow, push"); + schemeDeckLists.add(schemeDeckList); + schemeDeckPanels.add(schemeDeckPanel); + + // Commander deck list + FPanel commanderDeckPanel = new FPanel(); + commanderDeckPanel.setBorderToggle(false); + commanderDeckPanel.setLayout(new MigLayout(sectionConstraints)); + commanderDeckPanel.add(new FLabel.Builder().text("Select Commander deck:").build(), labelConstraints); + FList commanderDeckList = new FList(); + commanderDeckList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + + FScrollPane scrCommander = new FScrollPane(commanderDeckList, true, + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + commanderDeckPanel.add(scrCommander, "grow, push"); + commanderDeckLists.add(commanderDeckList); + commanderDeckPanels.add(commanderDeckPanel); + + // Planar deck list + FPanel planarDeckPanel = new FPanel(); + planarDeckPanel.setBorderToggle(false); + planarDeckPanel.setLayout(new MigLayout(sectionConstraints)); + planarDeckPanel.add(new FLabel.Builder().text("Select Planar deck:").build(), labelConstraints); + FList planarDeckList = new FList(); + planarDeckList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + + FScrollPane scrPlanes = new FScrollPane(planarDeckList, true, + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + planarDeckPanel.add(scrPlanes, "grow, push"); + planarDeckLists.add(planarDeckList); + planarDeckPanels.add(planarDeckPanel); + + // Vanguard avatar list + FPanel vgdDeckPanel = new FPanel(); + vgdDeckPanel.setBorderToggle(false); + + FList vgdAvatarList = new FList(); + vgdAvatarList.setListData(isPlayerAI(playerIndex) ? aiListData : humanListData); + vgdAvatarList.setSelectedIndex(0); + vgdAvatarList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + vgdAvatarList.addListSelectionListener(vgdLSListener); + FScrollPane scrAvatars = new FScrollPane(vgdAvatarList, true, + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + + CardDetailPanel vgdDetail = new CardDetailPanel(); + vgdAvatarDetails.add(vgdDetail); + + vgdDeckPanel.setLayout(new MigLayout(sectionConstraints)); + vgdDeckPanel.add(new FLabel.Builder().text("Select a Vanguard avatar:").build(), labelConstraints); + vgdDeckPanel.add(scrAvatars, "grow, push"); + vgdDeckPanel.add(vgdDetail, "h 200px, pushx, growx, hidemode 3"); + vgdAvatarLists.add(vgdAvatarList); + vgdPanels.add(vgdDeckPanel); + } + + protected void onDeckClicked(int iPlayer, DeckType type, Collection selectedDecks) { + String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME); + playerPanels.get(iPlayer).setDeckSelectorButtonText(text); + } + + /** Populates the deck panel with the focused player's deck choices. */ + private void populateDeckPanel(final GameType forGameType) { + decksFrame.removeAll(); + + if (playerPanelWithFocus.isOpen() || playerPanelWithFocus.isRemote()) { + return; + } + + if (GameType.Constructed == forGameType) { + decksFrame.add(deckChoosers.get(playerWithFocus), "grow, push"); + if (deckChoosers.get(playerWithFocus).getSelectedDeckType().toString().contains("Random")) { + final String strCheckboxConstraints = "h 30px!, gap 0 20px 0 0"; + decksFrame.add(cbSingletons, strCheckboxConstraints); + decksFrame.add(cbArtifacts, strCheckboxConstraints); + } + } else if (GameType.Archenemy == forGameType || GameType.ArchenemyRumble == forGameType) { + if (isPlayerArchenemy(playerWithFocus)) { + decksFrame.add(schemeDeckPanels.get(playerWithFocus), "grow, push"); + } else { + populateDeckPanel(GameType.Constructed); + } + } else if (GameType.Commander == forGameType || GameType.TinyLeaders == forGameType) { + decksFrame.add(commanderDeckPanels.get(playerWithFocus), "grow, push"); + } else if (GameType.Planechase == forGameType) { + decksFrame.add(planarDeckPanels.get(playerWithFocus), "grow, push"); + } else if (GameType.Vanguard == forGameType) { + updateVanguardList(playerWithFocus); + decksFrame.add(vgdPanels.get(playerWithFocus), "grow, push"); + } + refreshPanels(false, true); + } + + /** @return {@link javax.swing.JButton} */ + public JButton getBtnStart() { + return this.btnStart; + } + + public LblHeader getLblTitle() { return lblTitle; } + public JPanel getConstructedFrame() { return constructedFrame; } + public JPanel getPanelStart() { return pnlStart; } + public List getDeckChoosers() { return Collections.unmodifiableList(deckChoosers); } + + /** Gets the random deck checkbox for Singletons. */ + public FCheckBox getCbSingletons() { return cbSingletons; } + + /** Gets the random deck checkbox for Artifacts. */ + public FCheckBox getCbArtifacts() { return cbArtifacts; } + + public FCheckBox getVntArchenemy() { return vntArchenemy; } + public FCheckBox getVntArchenemyRumble() { return vntArchenemyRumble; } + public FCheckBox getVntCommander() { return vntCommander; } + public FCheckBox getVntMomirBasic() { return vntMomirBasic; } + public FCheckBox getVntPlanechase() { return vntPlanechase; } + public FCheckBox getVntTinyLeaders() { return vntTinyLeaders; } + public FCheckBox getVntVanguard() { return vntVanguard; } + + public int getLastArchenemy() { return lastArchenemy; } + public void setLastArchenemy(final int archenemy) { lastArchenemy = archenemy; } + + public final List getPlayerPanels() { + return playerPanels; + } + public final PlayerPanel getPlayerPanelWithFocus() { + return playerPanelWithFocus; + } + + public final FDeckChooser getDeckChooser(int playernum) { + return deckChoosers.get(playernum); + } + + public List getTeams() { return Collections.unmodifiableList(teams); } + public List getArchenemyTeams() { return Collections.unmodifiableList(archenemyTeams); } + public GameType getCurrentGameMode() { return currentGameMode; } + public void setCurrentGameMode(final GameType mode) { currentGameMode = mode; } + + public boolean isPlayerAI(int playernum) { + return playerPanels.get(playernum).isAi(); + } + + public Map getAiOptions(int playernum) { + if (playerPanels.get(playernum).isSimulatedAi()) { + Map options = new HashMap(); + options.put("UseSimulation", "True"); + return options; + } + return null; + } + + public int getNumPlayers() { + return activePlayersNum; + } + + public final List getParticipants() { + final List participants = new ArrayList(activePlayersNum); + for (final PlayerPanel panel : playerPanels) { + if (panel.isVisible()) { + participants.add(playerPanels.indexOf(panel)); + } + } + return participants; + } + + /** Revalidates the player and deck sections. Necessary after adding or hiding any panels. */ + private void refreshPanels(boolean refreshPlayerFrame, boolean refreshDeckFrame) { + if (refreshPlayerFrame) { + playersScroll.validate(); + playersScroll.repaint(); + } + if (refreshDeckFrame) { + decksFrame.validate(); + decksFrame.repaint(); + } + } + + public void changePlayerFocus(int newFocusOwner) { + changePlayerFocus(newFocusOwner, appliedVariants.contains(currentGameMode) ? currentGameMode : GameType.Constructed); + } + + void changePlayerFocus(int newFocusOwner, GameType gType) { + playerPanelWithFocus.setFocused(false); + playerWithFocus = newFocusOwner; + playerPanelWithFocus = playerPanels.get(playerWithFocus); + playerPanelWithFocus.setFocused(true); + + playersScroll.getViewport().scrollRectToVisible(playerPanelWithFocus.getBounds()); + populateDeckPanel(gType); + + refreshPanels(true, true); + } + + /** Saves avatar prefs for players one and two. */ + void updateAvatarPrefs() { + int pOneIndex = playerPanels.get(0).getAvatarIndex(); + int pTwoIndex = playerPanels.get(1).getAvatarIndex(); + + prefs.setPref(FPref.UI_AVATARS, pOneIndex + "," + pTwoIndex); + prefs.save(); + } + + /** Updates the avatars from preferences on update. */ + public void updatePlayersFromPrefs() { + ForgePreferences prefs = FModel.getPreferences(); + + // Avatar + String[] avatarPrefs = prefs.getPref(FPref.UI_AVATARS).split(","); + for (int i = 0; i < avatarPrefs.length; i++) { + int avatarIndex = Integer.parseInt(avatarPrefs[i]); + playerPanels.get(i).setAvatar(avatarIndex); + } + + // Name + String prefName = prefs.getPref(FPref.PLAYER_NAME); + playerPanels.get(0).setPlayerName(StringUtils.isBlank(prefName) ? "Human" : prefName); + } + + /** Adds a pre-styled FLabel component with the specified title. */ + FLabel newLabel(String title) { + return new FLabel.Builder().text(title).fontSize(14).fontStyle(Font.ITALIC).build(); + } + + List getUsedAvatars() { + List usedAvatars = Arrays.asList(-1,-1,-1,-1,-1,-1,-1,-1); + int i = 0; + for (PlayerPanel pp : playerPanels) { + usedAvatars.set(i++, pp.getAvatarIndex()); + } + return usedAvatars; + } + + final String getNewName() { + final String title = "Get new random name"; + final String message = "What type of name do you want to generate?"; + final SkinImage icon = FOptionPane.QUESTION_ICON; + final String[] genderOptions = new String[]{ "Male", "Female", "Any" }; + final String[] typeOptions = new String[]{ "Fantasy", "Generic", "Any" }; + + final int genderIndex = FOptionPane.showOptionDialog(message, title, icon, genderOptions, 2); + if (genderIndex < 0) { + return null; + } + final int typeIndex = FOptionPane.showOptionDialog(message, title, icon, typeOptions, 2); + if (typeIndex < 0) { + return null; + } + + final String gender = genderOptions[genderIndex]; + final String type = typeOptions[typeIndex]; + + String confirmMsg, newName; + List usedNames = getPlayerNames(); + do { + newName = NameGenerator.getRandomName(gender, type, usedNames); + confirmMsg = "Would you like to use the name \"" + newName + "\", or try again?"; + } while (!FOptionPane.showConfirmDialog(confirmMsg, title, "Use this name", "Try again", true)); + + return newName; + } + + List getPlayerNames() { + List names = new ArrayList(); + for (PlayerPanel pp : playerPanels) { + names.add(pp.getPlayerName()); + } + return names; + } + + public String getPlayerName(int i) { + return playerPanels.get(i).getPlayerName(); + } + + public int getPlayerAvatar(int i) { + return playerPanels.get(i).getAvatarIndex(); + } + + public boolean isEnoughTeams() { + int lastTeam = -1; + final List teamList = appliedVariants.contains(GameType.Archenemy) ? archenemyTeams : teams; + + for (final int i : getParticipants()) { + if (lastTeam == -1) { + lastTeam = teamList.get(i); + } else if (lastTeam != teamList.get(i)) { + return true; + } + } + return false; + } + + ///////////////////////////////////////////// + //========== Various listeners in build order + + @SuppressWarnings("serial") private class VariantCheckBox extends FCheckBox { + private final GameType variantType; + + private VariantCheckBox(GameType variantType0) { + super(variantType0.toString()); + + variantType = variantType0; + + setToolTipText(variantType.getDescription()); + + addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + appliedVariants.add(variantType); + currentGameMode = variantType; + + //ensure other necessary variants are unchecked + switch (variantType) { + case Archenemy: + vntArchenemyRumble.setSelected(false); + break; + case ArchenemyRumble: + vntArchenemy.setSelected(false); + break; + case Commander: + vntTinyLeaders.setSelected(false); + vntMomirBasic.setSelected(false); + break; + case TinyLeaders: + vntCommander.setSelected(false); + vntMomirBasic.setSelected(false); + break; + case Vanguard: + vntMomirBasic.setSelected(false); + break; + case MomirBasic: + vntCommander.setSelected(false); + vntVanguard.setSelected(false); + break; + default: + break; + } + } + else { + appliedVariants.remove(variantType); + if (currentGameMode == variantType) { + currentGameMode = GameType.Constructed; + } + } + + for (PlayerPanel pp : playerPanels) { + pp.toggleIsPlayerArchenemy(); + } + changePlayerFocus(playerWithFocus, currentGameMode); + } + }); + } + } + + ActionListener nameListener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + FTextField nField = (FTextField)e.getSource(); + nField.transferFocus(); + } + }; + + /** This listener will look for a vanguard avatar being selected in the lists + / and update the corresponding detail panel. */ + private ListSelectionListener vgdLSListener = new ListSelectionListener() { + + @Override + public void valueChanged(ListSelectionEvent e) { + int index = vgdAvatarLists.indexOf(e.getSource()); + Object obj = vgdAvatarLists.get(index).getSelectedValue(); + PlayerPanel pp = playerPanels.get(index); + CardDetailPanel cdp = vgdAvatarDetails.get(index); + + if (obj instanceof PaperCard) { + pp.setVanguardButtonText(((PaperCard) obj).getName()); + cdp.setCard(CardView.getCardForUi((PaperCard) obj)); + cdp.setVisible(true); + refreshPanels(false, true); + } + else { + pp.setVanguardButtonText((String) obj); + cdp.setVisible(false); + } + } + }; + + + ///////////////////////////////////// + //========== METHODS FOR VARIANTS + + public Set getAppliedVariants() { + return Collections.unmodifiableSet(appliedVariants); + } + + public int getTeam(final int playerIndex) { + return appliedVariants.contains(GameType.Archenemy) ? archenemyTeams.get(playerIndex) : teams.get(playerIndex); + } + + /** Gets the list of planar deck lists. */ + public List> getPlanarDeckLists() { + return planarDeckLists; + } + + /** Gets the list of commander deck lists. */ + public List> getCommanderDeckLists() { + return commanderDeckLists; + } + + /** Gets the list of scheme deck lists. */ + public List> getSchemeDeckLists() { + return schemeDeckLists; + } + + public boolean isPlayerArchenemy(final int playernum) { + return playerPanels.get(playernum).isArchenemy(); + } + + /** Gets the list of Vanguard avatar lists. */ + public List> getVanguardLists() { + return vgdAvatarLists; + } + + /** Return all the Vanguard avatars. */ + public Iterable getAllAvatars() { + if (vgdAllAvatars.isEmpty()) { + for (PaperCard c : FModel.getMagicDb().getVariantCards().getAllCards()) { + if (c.getRules().getType().isVanguard()) { + vgdAllAvatars.add(c); + } + } + } + return vgdAllAvatars; + } + + /** Return the Vanguard avatars not flagged RemAIDeck. */ + public List getAllAiAvatars() { + return vgdAllAiAvatars; + } + + /** Return the Vanguard avatars not flagged RemRandomDeck. */ + public List getNonRandomHumanAvatars() { + return nonRandomHumanAvatars; + } + + /** Return the Vanguard avatars not flagged RemAIDeck or RemRandomDeck. */ + public List getNonRandomAiAvatars() { + return nonRandomAiAvatars; + } + + /** Populate vanguard lists. */ + private void populateVanguardLists() { + humanListData.add("Use deck's default avatar (random if unavailable)"); + humanListData.add("Random"); + aiListData.add("Use deck's default avatar (random if unavailable)"); + aiListData.add("Random"); + for (PaperCard cp : getAllAvatars()) { + humanListData.add(cp); + if (!cp.getRules().getAiHints().getRemRandomDecks()) { + nonRandomHumanAvatars.add(cp); + } + if (!cp.getRules().getAiHints().getRemAIDecks()) { + aiListData.add(cp); + vgdAllAiAvatars.add(cp); + if (!cp.getRules().getAiHints().getRemRandomDecks()) { + nonRandomAiAvatars.add(cp); + } + } + } + } + + /** update vanguard list. */ + public void updateVanguardList(int playerIndex) { + FList vgdList = getVanguardLists().get(playerIndex); + Object lastSelection = vgdList.getSelectedValue(); + vgdList.setListData(isPlayerAI(playerIndex) ? aiListData : humanListData); + if (null != lastSelection) { + vgdList.setSelectedValue(lastSelection, true); + } + + if (-1 == vgdList.getSelectedIndex()) { + vgdList.setSelectedIndex(0); + } + } +} diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/online/COnlineLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/online/COnlineLobby.java new file mode 100644 index 00000000000..9e6c2f0fa11 --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/screens/home/online/COnlineLobby.java @@ -0,0 +1,41 @@ +package forge.screens.home.online; + +import forge.UiCommand; +import forge.gui.framework.ICDoc; +import forge.screens.home.CLobby; + +public enum COnlineLobby implements ICDoc { + SINGLETON_INSTANCE; + + private final VOnlineLobby view = VOnlineLobby.SINGLETON_INSTANCE; + private final CLobby lobby = new CLobby(view.getLobby()); + + @Override + public void register() { + } + + /* (non-Javadoc) + * @see forge.gui.home.ICSubmenu#initialize() + */ + @Override + public void update() { + lobby.update(); + } + + /* (non-Javadoc) + * @see forge.gui.home.ICSubmenu#initialize() + */ + @Override + public void initialize() { + lobby.initialize(); + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#getCommandOnSelect() + */ + @Override + public UiCommand getCommandOnSelect() { + return null; + } + +} diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/online/CSubmenuOnlineLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/online/CSubmenuOnlineLobby.java index 4b63f52fdc4..03dc85c2ca7 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/online/CSubmenuOnlineLobby.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/online/CSubmenuOnlineLobby.java @@ -1,48 +1,93 @@ package forge.screens.home.online; -import forge.UiCommand; -import forge.gui.framework.ICDoc; -import forge.menus.IMenuProvider; -import forge.menus.MenuUtil; - -import javax.swing.*; - import java.util.ArrayList; import java.util.List; +import javax.swing.JMenu; + +import forge.GuiBase; +import forge.Singletons; +import forge.UiCommand; +import forge.game.GameRules; +import forge.game.GameType; +import forge.gui.FNetOverlay; +import forge.gui.framework.FScreen; +import forge.gui.framework.ICDoc; +import forge.menus.IMenuProvider; +import forge.menus.MenuUtil; +import forge.model.FModel; +import forge.net.FGameClient; +import forge.net.FServerManager; +import forge.net.game.client.ILobbyListener; +import forge.net.game.server.RemoteClient; +import forge.properties.ForgePreferences.FPref; +import forge.screens.home.sanctioned.ConstructedGameMenu; public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider { SINGLETON_INSTANCE; - //private final VSubmenuOnlineLobby view = VSubmenuOnlineLobby.SINGLETON_INSTANCE; - - @Override - public void update() { - MenuUtil.setMenuProvider(this); - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { + final void host(final int portNumber) { + FServerManager.getInstance().startServer(portNumber); + FServerManager.getInstance().registerLobbyListener(new ILobbyListener() { + @Override public final void logout(final RemoteClient client) { + } + @Override public final void login(final RemoteClient client) { + VOnlineLobby.SINGLETON_INSTANCE.getLobby().addPlayerInFreeSlot(client.getUsername()); + } + @Override public final void message(final String source, final String message) { + FNetOverlay.SINGLETON_INSTANCE.addMessage(source, message); } }); + FServerManager.getInstance().hostGame(new GameRules(GameType.Constructed)); + + Singletons.getControl().setCurrentScreen(FScreen.ONLINE_LOBBY); + FNetOverlay.SINGLETON_INSTANCE.showUp("Hosting game"); + } + + final void join(final String hostname, final int port) { + final FGameClient client = new FGameClient(FModel.getPreferences().getPref(FPref.PLAYER_NAME), "0", GuiBase.getInterface().getNewGuiGame()); + FNetOverlay.SINGLETON_INSTANCE.setGameClient(client); + client.connect(hostname, port); + + Singletons.getControl().setCurrentScreen(FScreen.ONLINE_LOBBY); + FNetOverlay.SINGLETON_INSTANCE.showUp(String.format("Connected to %s:%s", hostname, port)); } @Override public void register() { } + /* (non-Javadoc) + * @see forge.gui.home.ICSubmenu#initialize() + */ + @Override + public void update() { + MenuUtil.setMenuProvider(this); + } + + /* (non-Javadoc) + * @see forge.gui.home.ICSubmenu#initialize() + */ @Override public void initialize() { } + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#getCommandOnSelect() + */ @Override public UiCommand getCommandOnSelect() { return null; } + /* (non-Javadoc) + * @see forge.gui.menubar.IMenuProvider#getMenus() + */ @Override public List getMenus() { List menus = new ArrayList(); + menus.add(ConstructedGameMenu.getMenu()); return menus; } + } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/online/VOnlineLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/online/VOnlineLobby.java new file mode 100644 index 00000000000..b9a4590b27d --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/screens/home/online/VOnlineLobby.java @@ -0,0 +1,105 @@ +package forge.screens.home.online; + +import javax.swing.JPanel; + +import net.miginfocom.swing.MigLayout; +import forge.deckchooser.DecksComboBoxEvent; +import forge.deckchooser.FDeckChooser; +import forge.deckchooser.IDecksComboBoxListener; +import forge.gui.framework.DragCell; +import forge.gui.framework.DragTab; +import forge.gui.framework.EDocID; +import forge.gui.framework.FScreen; +import forge.gui.framework.IVDoc; +import forge.gui.framework.IVTopLevelUI; +import forge.screens.home.VLobby; +import forge.toolbox.FPanel; +import forge.util.gui.SOptionPane; +import forge.view.FView; + +public enum VOnlineLobby implements IVDoc, IVTopLevelUI { + SINGLETON_INSTANCE; + + private DragCell parentCell; + private final DragTab tab = new DragTab("Lobby"); + + // General variables + private final VLobby lobby; + + private VOnlineLobby() { + this.lobby = new VLobby(true); + } + + VLobby getLobby() { + return lobby; + } + + @Override + public void populate() { + final JPanel outerContainer = FView.SINGLETON_INSTANCE.getPnlInsets(); + outerContainer.removeAll(); + final FPanel container = new FPanel(new MigLayout("insets 0, gap 0, wrap 1, ax right")); + outerContainer.add(container); + lobby.getLblTitle().setText("Online Multiplayer: Lobby"); + container.add(lobby.getLblTitle(), "w 80%, h 40px!, gap 0 0 15px 15px, span 2, al right, pushx"); + + for (final FDeckChooser fdc : lobby.getDeckChoosers()) { + fdc.populate(); + fdc.getDecksComboBox().addListener(new IDecksComboBoxListener() { + @Override public final void deckTypeSelected(final DecksComboBoxEvent ev) { + lobby.getPlayerPanelWithFocus().focusOnAvatar(); + } + }); + } + + container.add(lobby.getConstructedFrame(), "gap 20px 20px 20px 0px, push, grow"); + container.add(lobby.getPanelStart(), "gap 0 0 3.5%! 3.5%!, ax center"); + + if (container.isShowing()) { + container.validate(); + container.repaint(); + } + + lobby.changePlayerFocus(0); + } + + @Override + public EDocID getDocumentID() { + return EDocID.ONLINE_LOBBY; + } + + @Override + public DragTab getTabLabel() { + return tab; + } + + @Override + public COnlineLobby getLayoutControl() { + return COnlineLobby.SINGLETON_INSTANCE; + } + + @Override + public void setParentCell(final DragCell cell0) { + parentCell = cell0; + } + + @Override + public DragCell getParentCell() { + return parentCell; + } + + @Override + public void instantiate() { + } + + @Override + public boolean onSwitching(final FScreen fromScreen, final FScreen toScreen) { + return true; + } + + @Override + public boolean onClosing(final FScreen screen) { + return SOptionPane.showConfirmDialog("Leave lobby?", "Leave"); + } + +} diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/online/VSubmenuOnlineLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/online/VSubmenuOnlineLobby.java index 3cee053d4d6..d16e5daf22c 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/online/VSubmenuOnlineLobby.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/online/VSubmenuOnlineLobby.java @@ -1,65 +1,85 @@ package forge.screens.home.online; -import javax.swing.JButton; -import javax.swing.JPanel; -import net.miginfocom.swing.MigLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JPanel; + +import net.miginfocom.swing.MigLayout; import forge.gui.framework.DragCell; import forge.gui.framework.DragTab; import forge.gui.framework.EDocID; +import forge.properties.ForgeConstants; import forge.screens.home.EMenuGroup; import forge.screens.home.IVSubmenu; -import forge.screens.home.LblHeader; -import forge.screens.home.StartButton; import forge.screens.home.VHomeUI; -import forge.toolbox.FSkin; - +import forge.toolbox.FButton; +import forge.toolbox.FLabel; +import forge.toolbox.FPanel; +import forge.toolbox.FTextField; public enum VSubmenuOnlineLobby implements IVSubmenu { SINGLETON_INSTANCE; private DragCell parentCell; - private final DragTab tab = new DragTab("Lobby"); - - // General variables - private final LblHeader lblTitle = new LblHeader("Online Multiplayer: Lobby"); - - private final StartButton btnStart = new StartButton(); - private final JPanel pnlStart = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); - private final JPanel frame = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); // Main content frame + private final DragTab tab = new DragTab("Network Games"); private VSubmenuOnlineLobby() { - lblTitle.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2)); - - frame.setOpaque(false); - pnlStart.setOpaque(false); - pnlStart.add(btnStart, "align center"); } @Override public void populate() { - JPanel container = VHomeUI.SINGLETON_INSTANCE.getPnlDisplay(); + final JPanel container = VHomeUI.SINGLETON_INSTANCE.getPnlDisplay(); container.removeAll(); - container.setLayout(new MigLayout("insets 0, gap 0, wrap 1, ax right")); - container.add(lblTitle, "w 80%, h 40px!, gap 0 0 15px 15px, span 2, al right, pushx"); + container.setLayout(new MigLayout("fill", "[grow][grow]", "[grow]")); - VHomeUI.SINGLETON_INSTANCE.getPnlDisplay().add(frame, "gap 20px 20px 20px 0px, push, grow"); - VHomeUI.SINGLETON_INSTANCE.getPnlDisplay().add(pnlStart, "gap 0 0 3.5%! 3.5%!, ax center"); + final FPanel pnlHost = new FPanel(new MigLayout("insets 5px 10% 5px 10%, wrap 2", "[grow,l]10[grow,r]", "[grow,c][grow,c]")); + container.add(pnlHost, "west, w 50%!, h 100%!"); - if (container.isShowing()) { - container.validate(); - container.repaint(); - } - } + final FLabel lblServerPort = new FLabel.Builder().text("Server port").build(); + pnlHost.add(lblServerPort, "w 100!, h 50!"); - public JButton getBtnStart() { - return btnStart; + final FTextField txtServerPort = new FTextField.Builder().text(String.valueOf(ForgeConstants.SERVER_PORT_NUMBER)).build(); + txtServerPort.setEditable(false); + pnlHost.add(txtServerPort, "wrap"); + + final FButton btnHost = new FButton("Host"); + btnHost.addActionListener(new ActionListener() { + @Override public final void actionPerformed(final ActionEvent e) { + getLayoutControl().host(Integer.parseInt(txtServerPort.getText())); + } + }); + pnlHost.add(btnHost, "span 2, wrap, w 200!, h 50!"); + + final FPanel pnlJoin = new FPanel(new MigLayout("insets 5px 10% 5px 10%, wrap 2", "[grow,l]10[grow,r]", "[grow,c][grow,c][grow,c]")); + container.add(pnlJoin, "east, w 50%!, h 100%!"); + + final FLabel lblJoinHost = new FLabel.Builder().text("Hostname").build(); + pnlJoin.add(lblJoinHost, "w 100!, h 50!"); + + final FTextField txtJoinHost = new FTextField.Builder().text("localhost").build(); + pnlJoin.add(txtJoinHost, "wrap, w 250!"); + + final FLabel lblJoinPort = new FLabel.Builder().text("Host port").build(); + pnlJoin.add(lblJoinPort, "w 100!, h 50!"); + + final FTextField txtJoinPort = new FTextField.Builder().text(String.valueOf(ForgeConstants.SERVER_PORT_NUMBER)).build(); + txtJoinPort.setEditable(false); + pnlJoin.add(txtJoinPort, "wrap"); + + final FButton btnJoin = new FButton("Join"); + btnJoin.addActionListener(new ActionListener() { + @Override public final void actionPerformed(final ActionEvent e) { + getLayoutControl().join(txtJoinHost.getText(), Integer.parseInt(txtJoinPort.getText())); + } + }); + pnlJoin.add(btnJoin, "span 2, w 200!, h 50!"); } @Override public EMenuGroup getGroupEnum() { - return null; //EMenuGroup.ONLINE; + return EMenuGroup.ONLINE; } @Override @@ -69,12 +89,12 @@ public enum VSubmenuOnlineLobby implements IVSubmenu { @Override public EDocID getItemEnum() { - return EDocID.HOME_LOBBY; + return getDocumentID(); } @Override public EDocID getDocumentID() { - return EDocID.HOME_LOBBY; + return EDocID.HOME_NETWORK; } @Override diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuConstructed.java b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuConstructed.java index f21670e1a0f..fc4195df81c 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuConstructed.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/CSubmenuConstructed.java @@ -1,46 +1,15 @@ package forge.screens.home.sanctioned; -import forge.GuiBase; -import forge.LobbyPlayer; -import forge.UiCommand; -import forge.deck.CardPool; -import forge.deck.Deck; -import forge.deck.DeckFormat; -import forge.deck.DeckSection; -import forge.deck.DeckType; -import forge.deck.DeckgenUtil; -import forge.game.GameType; -import forge.game.player.RegisteredPlayer; -import forge.gui.GuiDialog; -import forge.gui.framework.ICDoc; -import forge.interfaces.IGuiGame; -import forge.item.PaperCard; -import forge.match.HostedMatch; -import forge.menus.IMenuProvider; -import forge.menus.MenuUtil; -import forge.model.CardCollections; -import forge.model.FModel; -import forge.player.GamePlayerUtil; -import forge.properties.ForgePreferences; -import forge.properties.ForgePreferences.FPref; -import forge.toolbox.FList; -import forge.toolbox.FOptionPane; -import forge.util.Aggregates; -import forge.util.storage.IStorage; - -import javax.swing.*; - -import com.beust.jcommander.internal.Maps; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Vector; -//import forge.gui.home.variant.VSubmenuVanguard; +import javax.swing.JMenu; + +import forge.UiCommand; +import forge.gui.framework.ICDoc; +import forge.menus.IMenuProvider; +import forge.menus.MenuUtil; +import forge.screens.home.CLobby; /** * Controls the constructed submenu in the home UI. @@ -53,6 +22,7 @@ public enum CSubmenuConstructed implements ICDoc, IMenuProvider { SINGLETON_INSTANCE; private final VSubmenuConstructed view = VSubmenuConstructed.SINGLETON_INSTANCE; + private final CLobby lobby = new CLobby(view.getLobby()); @Override public void register() { @@ -64,85 +34,7 @@ public enum CSubmenuConstructed implements ICDoc, IMenuProvider { @Override public void update() { MenuUtil.setMenuProvider(this); - - SwingUtilities.invokeLater(new Runnable() { - @Override public void run() { - final CardCollections cColl = FModel.getDecks(); - FList deckList; - Vector listData; - Object val; - - for (int i = 0; i < 8; i++) { - // Commander: reinit deck list and restore last selections (if any) - deckList = view.getCommanderDeckLists().get(i); - listData = new Vector(); - listData.add("Generate"); - if (cColl.getCommander().size() > 0) { - listData.add("Random"); - for (Deck comDeck : cColl.getCommander()) { - listData.add(comDeck); - } - } - val = deckList.getSelectedValue(); - deckList.setListData(listData); - if (null != val) { - deckList.setSelectedValue(val, true); - } - if (-1 == deckList.getSelectedIndex()) { - deckList.setSelectedIndex(0); - } // End Commander - - // Archenemy: reinit deck list and restore last selections (if any) - deckList = view.getSchemeDeckLists().get(i); - listData = new Vector(); - listData.add("Use deck's scheme section (random if unavailable)"); - listData.add("Generate"); - if (cColl.getScheme().size() > 0) { - listData.add("Random"); - for (Deck schemeDeck : cColl.getScheme()) { - listData.add(schemeDeck); - } - } - val = deckList.getSelectedValue(); - deckList.setListData(listData); - if (null != val) { - deckList.setSelectedValue(val, true); - } - if (-1 == deckList.getSelectedIndex()) { - deckList.setSelectedIndex(0); - } // End Archenemy - - // Planechase: reinit deck lists and restore last selections (if any) - deckList = view.getPlanarDeckLists().get(i); - listData = new Vector(); - - listData.add("Use deck's planes section (random if unavailable)"); - listData.add("Generate"); - if (cColl.getPlane().size() > 0) { - listData.add("Random"); - for (Deck planarDeck : cColl.getPlane()) { - listData.add(planarDeck); - } - } - - val = deckList.getSelectedValue(); - deckList.setListData(listData); - if (null != val) { - deckList.setSelectedValue(val, true); - } - - if (-1 == deckList.getSelectedIndex()) { - deckList.setSelectedIndex(0); - } // End Planechase - - view.updateVanguardList(i); - } - - // General updates when switching back to this view - view.updatePlayersFromPrefs(); - view.getBtnStart().requestFocusInWindow(); - } - }); + lobby.update(); } /* (non-Javadoc) @@ -150,248 +42,7 @@ public enum CSubmenuConstructed implements ICDoc, IMenuProvider { */ @Override public void initialize() { - view.getDeckChooser(0).initialize(FPref.CONSTRUCTED_P1_DECK_STATE, DeckType.PRECONSTRUCTED_DECK); - view.getDeckChooser(1).initialize(FPref.CONSTRUCTED_P2_DECK_STATE, DeckType.COLOR_DECK); - view.getDeckChooser(2).initialize(FPref.CONSTRUCTED_P3_DECK_STATE, DeckType.COLOR_DECK); - view.getDeckChooser(3).initialize(FPref.CONSTRUCTED_P4_DECK_STATE, DeckType.COLOR_DECK); - view.getDeckChooser(4).initialize(FPref.CONSTRUCTED_P5_DECK_STATE, DeckType.COLOR_DECK); - view.getDeckChooser(5).initialize(FPref.CONSTRUCTED_P6_DECK_STATE, DeckType.COLOR_DECK); - view.getDeckChooser(6).initialize(FPref.CONSTRUCTED_P7_DECK_STATE, DeckType.COLOR_DECK); - view.getDeckChooser(7).initialize(FPref.CONSTRUCTED_P8_DECK_STATE, DeckType.COLOR_DECK); - - // Start button event handling - view.getBtnStart().addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent arg0) { - startGame(view.getAppliedVariants()); - } - }); - - final ForgePreferences prefs = FModel.getPreferences(); - // Checkbox event handling - view.getCbSingletons().addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent arg0) { - prefs.setPref(FPref.DECKGEN_SINGLETONS, String.valueOf(view.getCbSingletons().isSelected())); - prefs.save(); - } - }); - - view.getCbArtifacts().addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent arg0) { - prefs.setPref(FPref.DECKGEN_ARTIFACTS, String.valueOf(view.getCbArtifacts().isSelected())); - prefs.save(); - } - }); - - // Pre-select checkboxes - view.getCbSingletons().setSelected(prefs.getPrefBoolean(FPref.DECKGEN_SINGLETONS)); - view.getCbArtifacts().setSelected(prefs.getPrefBoolean(FPref.DECKGEN_ARTIFACTS)); - } - - /** Starts a match with the applied variants. */ - private void startGame(final Set variantTypes) { - if (!view.isEnoughTeams()) { - FOptionPane.showMessageDialog("There are not enough teams! Please adjust team allocations."); - return; - } - - for (final int i : view.getParticipants()) { - if (view.getDeckChooser(i).getPlayer() == null) { - FOptionPane.showMessageDialog("Please specify a deck for " + view.getPlayerName(i)); - return; - } - } // Is it even possible anymore? I think current implementation assigns decks automatically. - - GameType autoGenerateVariant = null; - boolean isCommanderMatch = false; - boolean isTinyLeadersMatch = false; - if (!variantTypes.isEmpty()) { - isTinyLeadersMatch = variantTypes.contains(GameType.TinyLeaders); - isCommanderMatch = isTinyLeadersMatch || variantTypes.contains(GameType.Commander); - if (!isCommanderMatch) { - for (GameType variant : variantTypes) { - if (variant.isAutoGenerated()) { - autoGenerateVariant = variant; - break; - } - } - } - } - - boolean checkLegality = FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY); - - //Auto-generated decks don't need to be checked here - //Commander deck replaces regular deck and is checked later - if (checkLegality && autoGenerateVariant == null && !isCommanderMatch) { - for (final int i : view.getParticipants()) { - String name = view.getPlayerName(i); - String errMsg = GameType.Constructed.getDeckFormat().getDeckConformanceProblem(view.getDeckChooser(i).getPlayer().getDeck()); - if (null != errMsg) { - FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Deck"); - return; - } - } - } - - final List players = new ArrayList(); - final Map guis = Maps.newHashMap(); - final IGuiGame gui = GuiBase.getInterface().getNewGuiGame(); - for (final int i : view.getParticipants()) { - final String name = view.getPlayerName(i); - final boolean isAI = view.isPlayerAI(i); - final LobbyPlayer lobbyPlayer = isAI - ? GamePlayerUtil.createAiPlayer(name, view.getPlayerAvatar(i), view.getAiOptions(i)) - : GamePlayerUtil.getGuiPlayer(name, i); - RegisteredPlayer rp = view.getDeckChooser(i).getPlayer(); - - if (variantTypes.isEmpty()) { - rp.setTeamNumber(view.getTeam(i)); - players.add(rp.setPlayer(lobbyPlayer)); - } else { - Deck deck = null; - PaperCard vanguardAvatar = null; - if (isCommanderMatch) { - final Object selected = view.getCommanderDeckLists().get(i).getSelectedValue(); - if (selected instanceof String) { - final String sel = (String) selected; - final IStorage comDecks = FModel.getDecks().getCommander(); - if (sel.equals("Random") && comDecks.size() > 0) { - deck = Aggregates.random(comDecks); - } - } else { - deck = (Deck) selected; - } - GameType commanderGameType = isTinyLeadersMatch ? GameType.TinyLeaders : GameType.Commander; - if (deck == null) { //Can be null if player deselects the list selection or chose Generate - deck = DeckgenUtil.generateCommanderDeck(isAI, commanderGameType); - } - if (checkLegality) { - String errMsg = commanderGameType.getDeckFormat().getDeckConformanceProblem(deck); - if (null != errMsg) { - FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid " + commanderGameType + " Deck"); - return; - } - } - } else if (autoGenerateVariant != null) { - deck = autoGenerateVariant.autoGenerateDeck(rp); - CardPool avatarPool = deck.get(DeckSection.Avatar); - if (avatarPool != null) { - vanguardAvatar = avatarPool.get(0); - } - } - - // Initialise variables for other variants - deck = deck == null ? rp.getDeck() : deck; - Iterable schemes = null; - final boolean playerIsArchenemy = view.isPlayerArchenemy(i); - Iterable planes = null; - - //Archenemy - if (variantTypes.contains(GameType.ArchenemyRumble) - || (variantTypes.contains(GameType.Archenemy) && playerIsArchenemy)) { - Object selected = view.getSchemeDeckLists().get(i).getSelectedValue(); - CardPool schemePool = null; - if (selected instanceof String) { - String sel = (String) selected; - if (sel.contains("Use deck's scheme section")) { - if (deck.has(DeckSection.Schemes)) { - schemePool = deck.get(DeckSection.Schemes); - } else { - sel = "Random"; - } - } - IStorage sDecks = FModel.getDecks().getScheme(); - if (sel.equals("Random") && sDecks.size() != 0) { - schemePool = Aggregates.random(sDecks).get(DeckSection.Schemes); - } - } else { - schemePool = ((Deck) selected).get(DeckSection.Schemes); - } - if (schemePool == null) { //Can be null if player deselects the list selection or chose Generate - schemePool = DeckgenUtil.generateSchemePool(); - } - if (checkLegality) { - String errMsg = DeckFormat.getSchemeSectionConformanceProblem(schemePool); - if (null != errMsg) { - FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Scheme Deck"); - return; - } - } - schemes = schemePool.toFlatList(); - } - - //Planechase - if (variantTypes.contains(GameType.Planechase)) { - Object selected = view.getPlanarDeckLists().get(i).getSelectedValue(); - CardPool planePool = null; - if (selected instanceof String) { - String sel = (String) selected; - if (sel.contains("Use deck's planes section")) { - if (deck.has(DeckSection.Planes)) { - planePool = deck.get(DeckSection.Planes); - } else { - sel = "Random"; - } - } - IStorage pDecks = FModel.getDecks().getPlane(); - if (sel.equals("Random") && pDecks.size() != 0) { - planePool = Aggregates.random(pDecks).get(DeckSection.Planes); - } - } else { - planePool = ((Deck) selected).get(DeckSection.Planes); - } - if (planePool == null) { //Can be null if player deselects the list selection or chose Generate - planePool = DeckgenUtil.generatePlanarPool(); - } - if (checkLegality) { - String errMsg = DeckFormat.getPlaneSectionConformanceProblem(planePool); - if (null != errMsg) { - FOptionPane.showErrorDialog(name + "'s deck " + errMsg, "Invalid Planar Deck"); - return; - } - } - planes = planePool.toFlatList(); - } - - //Vanguard - if (variantTypes.contains(GameType.Vanguard)) { - Object selected = view.getVanguardLists().get(i).getSelectedValue(); - if (selected instanceof String) { - String sel = (String) selected; - if (sel.contains("Use deck's default avatar") && deck.has(DeckSection.Avatar)) { - vanguardAvatar = deck.get(DeckSection.Avatar).get(0); - } else { //Only other string is "Random" - if (isAI) { //AI - vanguardAvatar = Aggregates.random(view.getNonRandomAiAvatars()); - } else { //Human - vanguardAvatar = Aggregates.random(view.getNonRandomHumanAvatars()); - } - } - } else { - vanguardAvatar = (PaperCard)selected; - } - if (vanguardAvatar == null) { //ERROR! null if avatar deselected on list - GuiDialog.message("No Vanguard avatar selected for " + name - + ". Please choose one or disable the Vanguard variant"); - return; - } - } - - rp = RegisteredPlayer.forVariants(variantTypes, deck, schemes, playerIsArchenemy, planes, vanguardAvatar); - rp.setTeamNumber(view.getTeam(i)); - players.add(rp.setPlayer(lobbyPlayer)); - } - - if (!isAI) { - guis.put(rp, gui); - } - view.getDeckChooser(i).saveState(); - } - - final HostedMatch hostedMatch = GuiBase.getInterface().hostMatch(); - hostedMatch.startMatch(GameType.Constructed, variantTypes, players, guis); + lobby.initialize(); } /* (non-Javadoc) diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/VSubmenuConstructed.java b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/VSubmenuConstructed.java index 25ba40b5314..12ec7ac2a4d 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/VSubmenuConstructed.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/sanctioned/VSubmenuConstructed.java @@ -1,85 +1,18 @@ package forge.screens.home.sanctioned; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; -import java.awt.event.ItemEvent; -import java.awt.event.ItemListener; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.Vector; - -import javax.swing.ButtonGroup; -import javax.swing.JButton; -import javax.swing.JCheckBoxMenuItem; import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.ScrollPaneConstants; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; import net.miginfocom.swing.MigLayout; - -import org.apache.commons.lang3.StringUtils; - -import com.google.common.base.Predicate; - -import forge.Singletons; -import forge.UiCommand; -import forge.assets.FSkinProp; -import forge.deck.DeckProxy; -import forge.deck.DeckSection; -import forge.deck.DeckType; import forge.deckchooser.DecksComboBoxEvent; import forge.deckchooser.FDeckChooser; import forge.deckchooser.IDecksComboBoxListener; -import forge.game.GameType; -import forge.game.card.CardView; -import forge.gui.CardDetailPanel; import forge.gui.framework.DragCell; import forge.gui.framework.DragTab; import forge.gui.framework.EDocID; -import forge.gui.framework.FScreen; -import forge.item.PaperCard; -import forge.model.FModel; -import forge.properties.ForgePreferences; -import forge.properties.ForgePreferences.FPref; -import forge.screens.deckeditor.CDeckEditorUI; -import forge.screens.deckeditor.controllers.CEditorCommander; -import forge.screens.deckeditor.controllers.CEditorVariant; import forge.screens.home.EMenuGroup; import forge.screens.home.IVSubmenu; -import forge.screens.home.LblHeader; -import forge.screens.home.StartButton; +import forge.screens.home.VLobby; import forge.screens.home.VHomeUI; -import forge.toolbox.FCheckBox; -import forge.toolbox.FComboBox; -import forge.toolbox.FComboBoxWrapper; -import forge.toolbox.FLabel; -import forge.toolbox.FList; -import forge.toolbox.FMouseAdapter; -import forge.toolbox.FOptionPane; -import forge.toolbox.FPanel; -import forge.toolbox.FRadioButton; -import forge.toolbox.FScrollPane; -import forge.toolbox.FScrollPanel; -import forge.toolbox.FSkin; -import forge.toolbox.FSkin.SkinColor; -import forge.toolbox.FSkin.SkinImage; -import forge.toolbox.FTextField; -import forge.util.Lang; -import forge.util.MyRandom; -import forge.util.NameGenerator; /** * Assembles Swing components of constructed submenu singleton. @@ -89,1201 +22,19 @@ import forge.util.NameGenerator; */ public enum VSubmenuConstructed implements IVSubmenu { SINGLETON_INSTANCE; - private static final ForgePreferences prefs = FModel.getPreferences(); - private static final SkinColor unfocusedPlayerOverlay = FSkin.getColor(FSkin.Colors.CLR_OVERLAY).alphaColor(120); - - private final static int MAX_PLAYERS = 8; // Fields used with interface IVDoc private DragCell parentCell; private final DragTab tab = new DragTab("Constructed Mode"); - // General variables - private final LblHeader lblTitle = new LblHeader("Sanctioned Format: Constructed"); - private int activePlayersNum = 2; - private int playerWithFocus = 0; // index of the player that currently has focus - private PlayerPanel playerPanelWithFocus; - private GameType currentGameMode = GameType.Constructed; - private List teams = new ArrayList(MAX_PLAYERS); - private List archenemyTeams = new ArrayList(MAX_PLAYERS); - - private final StartButton btnStart = new StartButton(); - private final JPanel pnlStart = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); - private final JPanel constructedFrame = new JPanel(new MigLayout("insets 0, gap 0, wrap 2")); // Main content frame - - // Variants frame and variables - private final Set appliedVariants = new TreeSet(); - private final FPanel variantsPanel = new FPanel(new MigLayout("insets 10, gapx 10")); - private final VariantCheckBox vntVanguard = new VariantCheckBox(GameType.Vanguard); - private final VariantCheckBox vntMomirBasic = new VariantCheckBox(GameType.MomirBasic); - private final VariantCheckBox vntCommander = new VariantCheckBox(GameType.Commander); - private final VariantCheckBox vntTinyLeaders = new VariantCheckBox(GameType.TinyLeaders); - private final VariantCheckBox vntPlanechase = new VariantCheckBox(GameType.Planechase); - private final VariantCheckBox vntArchenemy = new VariantCheckBox(GameType.Archenemy); - private final VariantCheckBox vntArchenemyRumble = new VariantCheckBox(GameType.ArchenemyRumble); - - // Player frame elements - private final JPanel playersFrame = new JPanel(new MigLayout("insets 0, gap 0 5, wrap, hidemode 3")); - private final FScrollPanel playersScroll = new FScrollPanel(new MigLayout("insets 0, gap 0, wrap, hidemode 3"), true); - private final List playerPanels = new ArrayList(MAX_PLAYERS); - - private final List closePlayerBtnList = new ArrayList(6); - private final FLabel addPlayerBtn = new FLabel.ButtonBuilder().fontSize(14).text("Add a Player").build(); - - // Deck frame elements - private final JPanel decksFrame = new JPanel(new MigLayout("insets 0, gap 0, wrap, hidemode 3")); - private final List deckChoosers = new ArrayList(8); - private final FCheckBox cbSingletons = new FCheckBox("Singleton Mode"); - private final FCheckBox cbArtifacts = new FCheckBox("Remove Artifacts"); - - // Variants - private final List> schemeDeckLists = new ArrayList>(); - private final List schemeDeckPanels = new ArrayList(MAX_PLAYERS); - private int lastArchenemy = 0; - - private final List> commanderDeckLists = new ArrayList>(); - private final List commanderDeckPanels = new ArrayList(MAX_PLAYERS); - - private final List> planarDeckLists = new ArrayList>(); - private final List planarDeckPanels = new ArrayList(MAX_PLAYERS); - - private final List> vgdAvatarLists = new ArrayList>(); - private final List vgdPanels = new ArrayList(MAX_PLAYERS); - private final List vgdAvatarDetails = new ArrayList(); - private final List vgdAllAvatars = new ArrayList(); - private final List vgdAllAiAvatars = new ArrayList(); - private final List nonRandomHumanAvatars = new ArrayList(); - private final List nonRandomAiAvatars = new ArrayList(); - private Vector humanListData = new Vector(); - private Vector aiListData = new Vector(); - - // CTR + private final VLobby lobby = new VLobby(false); private VSubmenuConstructed() { - lblTitle.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2)); - - //////////////////////////////////////////////////////// - //////////////////// Variants Panel //////////////////// - - variantsPanel.setOpaque(false); - variantsPanel.add(newLabel("Variants:")); - variantsPanel.add(vntVanguard); - variantsPanel.add(vntMomirBasic); - variantsPanel.add(vntCommander); - variantsPanel.add(vntTinyLeaders); - variantsPanel.add(vntPlanechase); - variantsPanel.add(vntArchenemy); - variantsPanel.add(vntArchenemyRumble); - - constructedFrame.add(new FScrollPane(variantsPanel, false, true, - ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), - "w 100%, h 45px!, gapbottom 10px, spanx 2, wrap"); - - //////////////////////////////////////////////////////// - ///////////////////// Player Panel ///////////////////// - - // Construct individual player panels - String constraints = "pushx, growx, wrap, hidemode 3"; - for (int i = 0; i < MAX_PLAYERS; i++) { - teams.add(i + 1); - archenemyTeams.add(i == 0 ? 1 : 2); - - PlayerPanel player = new PlayerPanel(i); - playerPanels.add(player); - - // Populate players panel - player.setVisible(i < activePlayersNum); - - playersScroll.add(player, constraints); - - if (i == 0) { - constraints += ", gaptop 5px"; - } - } - - playerPanelWithFocus = playerPanels.get(0); - playerPanelWithFocus.setFocused(true); - - playersFrame.setOpaque(false); - playersFrame.add(playersScroll, "w 100%, h 100%-35px"); - - addPlayerBtn.setFocusable(true); - addPlayerBtn.setCommand(new Runnable() { - @Override - public void run() { - addPlayer(); - } - }); - playersFrame.add(addPlayerBtn, "height 30px!, growx, pushx"); - - constructedFrame.add(playersFrame, "gapright 10px, w 50%-5px, growy, pushy"); - - //////////////////////////////////////////////////////// - ////////////////////// Deck Panel ////////////////////// - - for (int i = 0; i < MAX_PLAYERS; i++) { - buildDeckPanel(i); - } - constructedFrame.add(decksFrame, "w 50%-5px, growy, pushy"); - constructedFrame.setOpaque(false); - decksFrame.setOpaque(false); - - // Start Button - pnlStart.setOpaque(false); - pnlStart.add(btnStart, "align center"); } - private void addPlayer() { - if (activePlayersNum >= MAX_PLAYERS) { - return; - } - - int freeIndex = -1; - for (int i = 0; i < MAX_PLAYERS; i++) { - if (!playerPanels.get(i).isVisible()) { - freeIndex = i; - break; - } - } - - playerPanels.get(freeIndex).setVisible(true); - - activePlayersNum++; - addPlayerBtn.setEnabled(activePlayersNum < MAX_PLAYERS); - - playerPanels.get(freeIndex).setVisible(true); - playerPanels.get(freeIndex).focusOnAvatar(); + public VLobby getLobby() { + return lobby; } - private void removePlayer(int playerIndex) { - activePlayersNum--; - FPanel player = playerPanels.get(playerIndex); - player.setVisible(false); - addPlayerBtn.setEnabled(true); - - //find closest player still in game and give focus - int min = MAX_PLAYERS; - int closest = 2; - - for (int participantIndex : getParticipants()) { - final int diff = Math.abs(playerIndex - participantIndex); - - if (diff < min) { - min = diff; - closest = participantIndex; - } - } - - changePlayerFocus(closest); - playerPanels.get(closest).focusOnAvatar(); - } - - /** Builds the actual deck panel layouts for each player. - * These are added to a list which can be referenced to populate the deck panel appropriately. */ - @SuppressWarnings("serial") - private void buildDeckPanel(final int playerIndex) { - String sectionConstraints = "insets 0, gap 0, wrap"; - String labelConstraints = "gaptop 10px, gapbottom 5px"; - - // Main deck - final FDeckChooser mainChooser = new FDeckChooser(null, isPlayerAI(playerIndex)); - mainChooser.initialize(); - mainChooser.getLstDecks().setSelectCommand(new UiCommand() { - @Override - public void run() { - VSubmenuConstructed.this.onDeckClicked(playerIndex, mainChooser.getSelectedDeckType(), mainChooser.getLstDecks().getSelectedItems()); - } - }); - deckChoosers.add(mainChooser); - - // Scheme deck list - FPanel schemeDeckPanel = new FPanel(); - schemeDeckPanel.setBorderToggle(false); - schemeDeckPanel.setLayout(new MigLayout(sectionConstraints)); - schemeDeckPanel.add(new FLabel.Builder().text("Select Scheme deck:").build(), labelConstraints); - FList schemeDeckList = new FList(); - schemeDeckList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); - - FScrollPane scrSchemes = new FScrollPane(schemeDeckList, true, - ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - schemeDeckPanel.add(scrSchemes, "grow, push"); - schemeDeckLists.add(schemeDeckList); - schemeDeckPanels.add(schemeDeckPanel); - - // Commander deck list - FPanel commanderDeckPanel = new FPanel(); - commanderDeckPanel.setBorderToggle(false); - commanderDeckPanel.setLayout(new MigLayout(sectionConstraints)); - commanderDeckPanel.add(new FLabel.Builder().text("Select Commander deck:").build(), labelConstraints); - FList commanderDeckList = new FList(); - commanderDeckList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); - - FScrollPane scrCommander = new FScrollPane(commanderDeckList, true, - ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - commanderDeckPanel.add(scrCommander, "grow, push"); - commanderDeckLists.add(commanderDeckList); - commanderDeckPanels.add(commanderDeckPanel); - - // Planar deck list - FPanel planarDeckPanel = new FPanel(); - planarDeckPanel.setBorderToggle(false); - planarDeckPanel.setLayout(new MigLayout(sectionConstraints)); - planarDeckPanel.add(new FLabel.Builder().text("Select Planar deck:").build(), labelConstraints); - FList planarDeckList = new FList(); - planarDeckList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); - - FScrollPane scrPlanes = new FScrollPane(planarDeckList, true, - ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - planarDeckPanel.add(scrPlanes, "grow, push"); - planarDeckLists.add(planarDeckList); - planarDeckPanels.add(planarDeckPanel); - - // Vanguard avatar list - FPanel vgdDeckPanel = new FPanel(); - vgdDeckPanel.setBorderToggle(false); - - FList vgdAvatarList = new FList(); - vgdAvatarList.setListData(isPlayerAI(playerIndex) ? aiListData : humanListData); - vgdAvatarList.setSelectedIndex(0); - vgdAvatarList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); - vgdAvatarList.addListSelectionListener(vgdLSListener); - FScrollPane scrAvatars = new FScrollPane(vgdAvatarList, true, - ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - - CardDetailPanel vgdDetail = new CardDetailPanel(); - vgdAvatarDetails.add(vgdDetail); - - vgdDeckPanel.setLayout(new MigLayout(sectionConstraints)); - vgdDeckPanel.add(new FLabel.Builder().text("Select a Vanguard avatar:").build(), labelConstraints); - vgdDeckPanel.add(scrAvatars, "grow, push"); - vgdDeckPanel.add(vgdDetail, "h 200px, pushx, growx, hidemode 3"); - vgdAvatarLists.add(vgdAvatarList); - vgdPanels.add(vgdDeckPanel); - } - - protected void onDeckClicked(int iPlayer, DeckType type, Collection selectedDecks) { - String text = type.toString() + ": " + Lang.joinHomogenous(selectedDecks, DeckProxy.FN_GET_NAME); - playerPanels.get(iPlayer).setDeckSelectorButtonText(text); - } - - /** Populates the deck panel with the focused player's deck choices. */ - private void populateDeckPanel(final GameType forGameType) { - decksFrame.removeAll(); - - if (GameType.Constructed == forGameType) { - decksFrame.add(deckChoosers.get(playerWithFocus), "grow, push"); - if (deckChoosers.get(playerWithFocus).getSelectedDeckType().toString().contains("Random")) { - final String strCheckboxConstraints = "h 30px!, gap 0 20px 0 0"; - decksFrame.add(cbSingletons, strCheckboxConstraints); - decksFrame.add(cbArtifacts, strCheckboxConstraints); - } - } - else if (GameType.Archenemy == forGameType || GameType.ArchenemyRumble == forGameType) { - if (isPlayerArchenemy(playerWithFocus)) { - decksFrame.add(schemeDeckPanels.get(playerWithFocus), "grow, push"); - } else { - populateDeckPanel(GameType.Constructed); - } - } - else if (GameType.Commander == forGameType || GameType.TinyLeaders == forGameType) { - decksFrame.add(commanderDeckPanels.get(playerWithFocus), "grow, push"); - } - else if (GameType.Planechase == forGameType) { - decksFrame.add(planarDeckPanels.get(playerWithFocus), "grow, push"); - } - else if (GameType.Vanguard == forGameType) { - updateVanguardList(playerWithFocus); - decksFrame.add(vgdPanels.get(playerWithFocus), "grow, push"); - } - refreshPanels(false, true); - } - - /* (non-Javadoc) - * @see forge.gui.home.IVSubmenu#getGroupEnum() - */ - @Override - public EMenuGroup getGroupEnum() { - return EMenuGroup.SANCTIONED; - } - - public final FDeckChooser getDeckChooser(int playernum) { - return deckChoosers.get(playernum); - } - - /* (non-Javadoc) - * @see forge.gui.home.IVSubmenu#getMenuTitle() - */ - @Override - public String getMenuTitle() { - return "Constructed"; - } - - /* (non-Javadoc) - * @see forge.gui.home.IVSubmenu#getItemEnum() - */ - @Override - public EDocID getItemEnum() { - return EDocID.HOME_CONSTRUCTED; - } - - /* (non-Javadoc) - * @see forge.gui.home.IVSubmenu#populate() - */ - @Override - public void populate() { - JPanel container = VHomeUI.SINGLETON_INSTANCE.getPnlDisplay(); - - container.removeAll(); - container.setLayout(new MigLayout("insets 0, gap 0, wrap 1, ax right")); - container.add(lblTitle, "w 80%, h 40px!, gap 0 0 15px 15px, span 2, al right, pushx"); - - for (final FDeckChooser fdc : deckChoosers) { - fdc.populate(); - fdc.getDecksComboBox().addListener(new IDecksComboBoxListener() { - @Override - public void deckTypeSelected(DecksComboBoxEvent ev) { - playerPanelWithFocus.focusOnAvatar(); - } - }); - } - populateDeckPanel(GameType.Constructed); - populateVanguardLists(); - - VHomeUI.SINGLETON_INSTANCE.getPnlDisplay().add(constructedFrame, "gap 20px 20px 20px 0px, push, grow"); - VHomeUI.SINGLETON_INSTANCE.getPnlDisplay().add(pnlStart, "gap 0 0 3.5%! 3.5%!, ax center"); - - if (container.isShowing()) { - container.validate(); - container.repaint(); - } - - changePlayerFocus(playerWithFocus, currentGameMode); - } - - /** @return {@link javax.swing.JButton} */ - public JButton getBtnStart() { - return this.btnStart; - } - - /** Gets the random deck checkbox for Singletons. */ - public FCheckBox getCbSingletons() { return cbSingletons; } - - /** Gets the random deck checkbox for Artifacts. */ - public FCheckBox getCbArtifacts() { return cbArtifacts; } - - public boolean isPlayerAI(int playernum) { - return playerPanels.get(playernum).isAi(); - } - - public Map getAiOptions(int playernum) { - if (playerPanels.get(playernum).isSimulatedAi()) { - Map options = new HashMap(); - options.put("UseSimulation", "True"); - return options; - } - return null; - } - - public int getNumPlayers() { - return activePlayersNum; - } - - public final List getParticipants() { - final List participants = new ArrayList(activePlayersNum); - for (final PlayerPanel panel : playerPanels) { - if (panel.isVisible()) { - participants.add(playerPanels.indexOf(panel)); - } - } - return participants; - } - - /** Revalidates the player and deck sections. Necessary after adding or hiding any panels. */ - private void refreshPanels(boolean refreshPlayerFrame, boolean refreshDeckFrame) { - if (refreshPlayerFrame) { - playersScroll.validate(); - playersScroll.repaint(); - } - if (refreshDeckFrame) { - decksFrame.validate(); - decksFrame.repaint(); - } - } - - @SuppressWarnings("serial") - private class PlayerPanel extends FPanel { - private final int index; - - private final FLabel nameRandomiser; - private final FLabel avatarLabel = new FLabel.Builder().opaque(true).hoverable(true).iconScaleFactor(0.99f).iconInBackground(true).build(); - private int avatarIndex; - - private final FTextField txtPlayerName = new FTextField.Builder().text("Player name").build(); - private FRadioButton radioHuman; - private FRadioButton radioAi; - private JCheckBoxMenuItem radioAiUseSimulation; - - private FComboBoxWrapper teamComboBox = new FComboBoxWrapper(); - private FComboBoxWrapper aeTeamComboBox = new FComboBoxWrapper(); - - private final FLabel deckBtn = new FLabel.ButtonBuilder().text("Select a deck").build(); - private final FLabel deckLabel = newLabel("Deck:"); - - private final String variantBtnConstraints = "height 30px, hidemode 3"; - - private boolean playerIsArchenemy = false; - private final FLabel scmDeckSelectorBtn = new FLabel.ButtonBuilder().text("Select a scheme deck").build(); - private final FLabel scmDeckEditor = new FLabel.ButtonBuilder().text("Scheme Deck Editor").build(); - private final FLabel scmLabel = newLabel("Scheme deck:"); - - private final FLabel cmdDeckSelectorBtn = new FLabel.ButtonBuilder().text("Select a Commander deck").build(); - private final FLabel cmdDeckEditor = new FLabel.ButtonBuilder().text("Commander Deck Editor").build(); - private final FLabel cmdLabel = newLabel("Commander deck:"); - - private final FLabel pchDeckSelectorBtn = new FLabel.ButtonBuilder().text("Select a planar deck").build(); - private final FLabel pchDeckEditor = new FLabel.ButtonBuilder().text("Planar Deck Editor").build(); - private final FLabel pchLabel = newLabel("Planar deck:"); - - private final FLabel vgdSelectorBtn = new FLabel.ButtonBuilder().text("Select a Vanguard avatar").build(); - private final FLabel vgdLabel = newLabel("Vanguard:"); - - public PlayerPanel(final int index) { - super(); - this.index = index; - playerIsArchenemy = index == 0; - - setLayout(new MigLayout("insets 10px, gap 5px")); - - // Add a button to players 3+ to remove them from the setup - if (index >= 2) { - FLabel closeBtn = createCloseButton(); - this.add(closeBtn, "w 20, h 20, pos (container.w-20) 0"); - } - - createAvatar(); - this.add(avatarLabel, "spany 2, width 80px, height 80px"); - - createNameEditor(); - this.add(newLabel("Name:"), "w 40px, h 30px, gaptop 5px"); - this.add(txtPlayerName, "h 30px, pushx, growx"); - - nameRandomiser = createNameRandomizer(); - this.add(nameRandomiser, "h 30px, w 30px, gaptop 5px"); - - createPlayerTypeOptions(); - this.add(radioHuman, "gapright 5px"); - this.add(radioAi, "wrap"); - - this.add(newLabel("Team:"), "w 40px, h 30px"); - populateTeamsComboBoxes(); - teamComboBox.addActionListener(teamListener); - aeTeamComboBox.addActionListener(teamListener); - teamComboBox.addTo(this, variantBtnConstraints + ", pushx, growx, gaptop 5px"); - aeTeamComboBox.addTo(this, variantBtnConstraints + ", pushx, growx, gaptop 5px"); - - this.add(deckLabel, variantBtnConstraints + ", cell 0 2, sx 2, ax right"); - this.add(deckBtn, variantBtnConstraints + ", cell 2 2, pushx, growx, wmax 100%-153px, h 30px, spanx 4, wrap"); - - addHandlersDeckSelector(); - - this.add(cmdLabel, variantBtnConstraints + ", cell 0 3, sx 2, ax right"); - this.add(cmdDeckSelectorBtn, variantBtnConstraints + ", cell 2 3, growx, pushx"); - this.add(cmdDeckEditor, variantBtnConstraints + ", cell 3 3, sx 3, growx, wrap"); - - this.add(scmLabel, variantBtnConstraints + ", cell 0 4, sx 2, ax right"); - this.add(scmDeckSelectorBtn, variantBtnConstraints + ", cell 2 4, growx, pushx"); - this.add(scmDeckEditor, variantBtnConstraints + ", cell 3 4, sx 3, growx, wrap"); - - this.add(pchLabel, variantBtnConstraints + ", cell 0 5, sx 2, ax right"); - this.add(pchDeckSelectorBtn, variantBtnConstraints + ", cell 2 5, growx, pushx"); - this.add(pchDeckEditor, variantBtnConstraints + ", cell 3 5, sx 3, growx, wrap"); - - this.add(vgdLabel, variantBtnConstraints + ", cell 0 6, sx 2, ax right"); - this.add(vgdSelectorBtn, variantBtnConstraints + ", cell 2 6, sx 4, growx, wrap"); - - addHandlersToVariantsControls(); - updateVariantControlsVisibility(); - - this.addMouseListener(new FMouseAdapter() { - @Override - public void onLeftMouseDown(MouseEvent e) { - avatarLabel.requestFocusInWindow(); - } - }); - } - - private final FMouseAdapter radioMouseAdapter = new FMouseAdapter() { - @Override - public void onLeftClick(MouseEvent e) { - avatarLabel.requestFocusInWindow(); - updateVanguardList(index); - } - }; - - /** Listens to name text fields and gives the appropriate player focus. - * Also saves the name preference when leaving player one's text field. */ - private FocusAdapter nameFocusListener = new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - changePlayerFocus(index); - } - - @Override - public void focusLost(FocusEvent e) { - final Object source = e.getSource(); - if (source instanceof FTextField) { // the text box - FTextField nField = (FTextField)source; - String newName = nField.getText().trim(); - if (index == 0 && !StringUtils.isBlank(newName) - && StringUtils.isAlphanumericSpace(newName) && prefs.getPref(FPref.PLAYER_NAME) != newName) { - prefs.setPref(FPref.PLAYER_NAME, newName); - prefs.save(); - } - } - } - }; - - /** Listens to avatar buttons and gives the appropriate player focus. */ - private FocusAdapter avatarFocusListener = new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - changePlayerFocus(index); - } - }; - - private FMouseAdapter avatarMouseListener = new FMouseAdapter() { - @Override - public void onLeftClick(MouseEvent e) { - final FLabel avatar = (FLabel)e.getSource(); - - changePlayerFocus(index); - avatar.requestFocusInWindow(); - - final AvatarSelector aSel = new AvatarSelector(getPlayerName(), avatarIndex, getUsedAvatars()); - for (final FLabel lbl : aSel.getSelectables()) { - lbl.setCommand(new UiCommand() { - @Override - public void run() { - setAvatar(Integer.valueOf(lbl.getName().substring(11))); - aSel.setVisible(false); - } - }); - } - - aSel.setVisible(true); - aSel.dispose(); - - if (index < 2) { - updateAvatarPrefs(); - } - } - @Override - public void onRightClick(MouseEvent e) { - changePlayerFocus(index); - avatarLabel.requestFocusInWindow(); - - setRandomAvatar(); - - if (index < 2) { - updateAvatarPrefs(); - } - } - }; - - public void updateVariantControlsVisibility() { - boolean isCommanderApplied = false; - boolean isPlanechaseApplied = false; - boolean isVanguardApplied = false; - boolean isArchenemyApplied = false; - boolean archenemyVisiblity = false; - boolean isDeckBuildingAllowed = true; - - for (GameType variant : appliedVariants) { - switch (variant) { - case Archenemy: - isArchenemyApplied = true; - if (playerIsArchenemy) { - archenemyVisiblity = true; - } - break; - case ArchenemyRumble: - archenemyVisiblity = true; - break; - case Commander: - case TinyLeaders: - isCommanderApplied = true; - isDeckBuildingAllowed = false; //Commander deck replaces basic deck, so hide that - break; - case Planechase: - isPlanechaseApplied = true; - break; - case Vanguard: - isVanguardApplied = true; - break; - default: - if (variant.isAutoGenerated()) { - isDeckBuildingAllowed = false; - } - break; - } - } - - deckLabel.setVisible(isDeckBuildingAllowed); - deckBtn.setVisible(isDeckBuildingAllowed); - cmdDeckSelectorBtn.setVisible(isCommanderApplied); - cmdDeckEditor.setVisible(isCommanderApplied); - cmdLabel.setVisible(isCommanderApplied); - - scmDeckSelectorBtn.setVisible(archenemyVisiblity); - scmDeckEditor.setVisible(archenemyVisiblity); - scmLabel.setVisible(archenemyVisiblity); - - teamComboBox.setVisible(!isArchenemyApplied); - aeTeamComboBox.setVisible(isArchenemyApplied); - aeTeamComboBox.setEnabled(!(isArchenemyApplied && playerIsArchenemy)); - - pchDeckSelectorBtn.setVisible(isPlanechaseApplied); - pchDeckEditor.setVisible(isPlanechaseApplied); - pchLabel.setVisible(isPlanechaseApplied); - - vgdSelectorBtn.setVisible(isVanguardApplied); - vgdLabel.setVisible(isVanguardApplied); - } - - @Override - public void paintComponent(Graphics g) { - super.paintComponent(g); - if (playerPanelWithFocus != this) { - FSkin.setGraphicsColor(g, unfocusedPlayerOverlay); - g.fillRect(0, 0, this.getWidth(), this.getHeight()); - } - } - - public boolean isAi() { - return radioAi.isSelected(); - } - - public boolean isSimulatedAi() { - return radioAi.isSelected() && radioAiUseSimulation.isSelected(); - } - - public void setVanguardButtonText(String text) { - vgdSelectorBtn.setText(text); - } - - public void setDeckSelectorButtonText(String text) { - deckBtn.setText(text); - } - - public void focusOnAvatar() { - avatarLabel.requestFocusInWindow(); - } - - private void populateTeamsComboBoxes() { - aeTeamComboBox.addItem("Archenemy"); - aeTeamComboBox.addItem("Heroes"); - aeTeamComboBox.setSelectedIndex(archenemyTeams.get(index) - 1); - aeTeamComboBox.setEnabled(playerIsArchenemy); - - for (int i = 1; i <= MAX_PLAYERS; i++) { - teamComboBox.addItem(i); - } - teamComboBox.setSelectedIndex(teams.get(index) - 1); - teamComboBox.setEnabled(true); - } - - private ActionListener teamListener = new ActionListener() { - @SuppressWarnings("unchecked") - @Override - public void actionPerformed(ActionEvent e) { - FComboBox cb = (FComboBox)e.getSource(); - cb.requestFocusInWindow(); - Object selection = cb.getSelectedItem(); - - if (null == selection) { - return; - } - if (appliedVariants.contains(GameType.Archenemy)) { - String sel = (String) selection; - if (sel.contains("Archenemy")) { - lastArchenemy = index; - for (PlayerPanel pp : playerPanels) { - int i = pp.index; - archenemyTeams.set(i, i == lastArchenemy ? 1 : 2); - pp.aeTeamComboBox.setSelectedIndex(i == lastArchenemy ? 0 : 1); - pp.toggleIsPlayerArchenemy(); - } - } - } else { - Integer sel = (Integer) selection; - teams.set(index, sel); - } - - changePlayerFocus(index); - } - }; - - public void toggleIsPlayerArchenemy() { - if (appliedVariants.contains(GameType.Archenemy)) { - playerIsArchenemy = lastArchenemy == index; - } - else { - playerIsArchenemy = appliedVariants.contains(GameType.ArchenemyRumble); - } - updateVariantControlsVisibility(); - } - - /** - * @param index - */ - private void addHandlersToVariantsControls() { - // Archenemy buttons - scmDeckSelectorBtn.setCommand(new Runnable() { - @Override - public void run() { - currentGameMode = vntArchenemy.isSelected() ? GameType.Archenemy : GameType.ArchenemyRumble; - scmDeckSelectorBtn.requestFocusInWindow(); - changePlayerFocus(index, currentGameMode); - } - }); - - scmDeckEditor.setCommand(new UiCommand() { - @Override - public void run() { - currentGameMode = vntArchenemy.isSelected() ? GameType.Archenemy : GameType.ArchenemyRumble; - Predicate predSchemes = new Predicate() { - @Override - public boolean apply(PaperCard arg0) { - return arg0.getRules().getType().isScheme(); - } - }; - - Singletons.getControl().setCurrentScreen(FScreen.DECK_EDITOR_ARCHENEMY); - CDeckEditorUI.SINGLETON_INSTANCE.setEditorController( - new CEditorVariant(FModel.getDecks().getScheme(), predSchemes, DeckSection.Schemes, FScreen.DECK_EDITOR_ARCHENEMY, CDeckEditorUI.SINGLETON_INSTANCE.getCDetailPicture())); - } - }); - - // Commander buttons - cmdDeckSelectorBtn.setCommand(new Runnable() { - @Override - public void run() { - currentGameMode = vntTinyLeaders.isSelected() ? GameType.TinyLeaders : GameType.Commander; - cmdDeckSelectorBtn.requestFocusInWindow(); - changePlayerFocus(index, currentGameMode); - } - }); - - cmdDeckEditor.setCommand(new UiCommand() { - @Override - public void run() { - currentGameMode = vntTinyLeaders.isSelected() ? GameType.TinyLeaders : GameType.Commander; - Singletons.getControl().setCurrentScreen(FScreen.DECK_EDITOR_COMMANDER); - CDeckEditorUI.SINGLETON_INSTANCE.setEditorController(new CEditorCommander(CDeckEditorUI.SINGLETON_INSTANCE.getCDetailPicture())); - } - }); - - // Planechase buttons - pchDeckSelectorBtn.setCommand(new Runnable() { - @Override - public void run() { - currentGameMode = GameType.Planechase; - pchDeckSelectorBtn.requestFocusInWindow(); - changePlayerFocus(index, GameType.Planechase); - } - }); - - pchDeckEditor.setCommand(new UiCommand() { - @Override - public void run() { - currentGameMode = GameType.Planechase; - Predicate predPlanes = new Predicate() { - @Override - public boolean apply(PaperCard arg0) { - return arg0.getRules().getType().isPlane() || arg0.getRules().getType().isPhenomenon(); - } - }; - - Singletons.getControl().setCurrentScreen(FScreen.DECK_EDITOR_PLANECHASE); - CDeckEditorUI.SINGLETON_INSTANCE.setEditorController( - new CEditorVariant(FModel.getDecks().getPlane(), predPlanes, DeckSection.Planes, FScreen.DECK_EDITOR_PLANECHASE, CDeckEditorUI.SINGLETON_INSTANCE.getCDetailPicture())); - } - }); - - // Vanguard buttons - vgdSelectorBtn.setCommand(new Runnable() { - @Override - public void run() { - currentGameMode = GameType.Vanguard; - vgdSelectorBtn.requestFocusInWindow(); - changePlayerFocus(index, GameType.Vanguard); - } - }); - } - - /** - * @param index - */ - private void createPlayerTypeOptions() { - radioHuman = new FRadioButton("Human", index == 0); - radioAi = new FRadioButton("AI", index != 0); - JPopupMenu menu = new JPopupMenu(); - radioAiUseSimulation = new JCheckBoxMenuItem("Use Simulation"); - menu.add(radioAiUseSimulation); - radioAi.setComponentPopupMenu(menu); - - radioHuman.addMouseListener(radioMouseAdapter); - radioAi.addMouseListener(radioMouseAdapter); - - ButtonGroup tempBtnGroup = new ButtonGroup(); - tempBtnGroup.add(radioHuman); - tempBtnGroup.add(radioAi); - } - - /** - * @param index - */ - private void addHandlersDeckSelector() { - deckBtn.setCommand(new Runnable() { - @Override - public void run() { - currentGameMode = GameType.Constructed; - deckBtn.requestFocusInWindow(); - changePlayerFocus(index, GameType.Constructed); - } - }); - } - - /** - * @param index - * @return - */ - private FLabel createNameRandomizer() { - final FLabel newNameBtn = new FLabel.Builder().tooltip("Get a new random name").iconInBackground(false) - .icon(FSkin.getIcon(FSkinProp.ICO_EDIT)).hoverable(true).opaque(false) - .unhoveredAlpha(0.9f).build(); - newNameBtn.setCommand(new UiCommand() { - @Override - public void run() { - String newName = getNewName(); - if (null == newName) { - return; - } - txtPlayerName.setText(newName); - - if (index == 0) { - prefs.setPref(FPref.PLAYER_NAME, newName); - prefs.save(); - } - txtPlayerName.requestFocus(); - changePlayerFocus(index); - } - }); - newNameBtn.addFocusListener(nameFocusListener); - return newNameBtn; - } - - /** - * @param index - * @return - */ - private void createNameEditor() { - String name; - if (index == 0) { - name = FModel.getPreferences().getPref(FPref.PLAYER_NAME); - if (name.isEmpty()) { - name = "Human"; - } - } - else { - name = NameGenerator.getRandomName("Any", "Any", getPlayerNames()); - } - - txtPlayerName.setText(name); - txtPlayerName.setFocusable(true); - txtPlayerName.setFont(FSkin.getFont(14)); - txtPlayerName.addActionListener(nameListener); - txtPlayerName.addFocusListener(nameFocusListener); - } - - private FLabel createCloseButton() { - final FLabel closeBtn = new FLabel.Builder().tooltip("Close").iconInBackground(false) - .icon(FSkin.getIcon(FSkinProp.ICO_CLOSE)).hoverable(true).build(); - closeBtn.setCommand(new Runnable() { - @Override - public void run() { - removePlayer(closePlayerBtnList.indexOf(closeBtn) + 2); - } - }); - closePlayerBtnList.add(closeBtn); - return closeBtn; - } - - private void createAvatar() { - String[] currentPrefs = FModel.getPreferences().getPref(FPref.UI_AVATARS).split(","); - if (index < currentPrefs.length) { - avatarIndex = Integer.parseInt(currentPrefs[index]); - avatarLabel.setIcon(FSkin.getAvatars().get(avatarIndex)); - } - else { - setRandomAvatar(); - } - - avatarLabel.setToolTipText("L-click: Select avatar. R-click: Randomize avatar."); - avatarLabel.addFocusListener(avatarFocusListener); - avatarLabel.addMouseListener(avatarMouseListener); - } - - /** Applies a random avatar, avoiding avatars already used. - * @param playerIndex */ - public void setRandomAvatar() { - int random = 0; - - List usedAvatars = getUsedAvatars(); - do { - random = MyRandom.getRandom().nextInt(FSkin.getAvatars().size()); - } while (usedAvatars.contains(random)); - setAvatar(random); - } - - public void setAvatar(int newAvatarIndex) { - avatarIndex = newAvatarIndex; - SkinImage icon = FSkin.getAvatars().get(newAvatarIndex); - avatarLabel.setIcon(icon); - avatarLabel.repaintSelf(); - } - - private final FSkin.LineSkinBorder focusedBorder = new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_BORDERS).alphaColor(255), 3); - private final FSkin.LineSkinBorder defaultBorder = new FSkin.LineSkinBorder(FSkin.getColor(FSkin.Colors.CLR_THEME).alphaColor(200), 2); - - public void setFocused(boolean focused) { - avatarLabel.setBorder(focused ? focusedBorder : defaultBorder); - avatarLabel.setHoverable(focused); - } - - public int getAvatarIndex() { - return avatarIndex; - } - - public void setPlayerName(String string) { - txtPlayerName.setText(string); - } - - public String getPlayerName() { - return txtPlayerName.getText(); - } - } - - private void changePlayerFocus(int newFocusOwner) { - changePlayerFocus(newFocusOwner, appliedVariants.contains(currentGameMode) ? currentGameMode : GameType.Constructed); - } - - private void changePlayerFocus(int newFocusOwner, GameType gType) { - playerPanelWithFocus.setFocused(false); - playerWithFocus = newFocusOwner; - playerPanelWithFocus = playerPanels.get(playerWithFocus); - playerPanelWithFocus.setFocused(true); - - playersScroll.getViewport().scrollRectToVisible(playerPanelWithFocus.getBounds()); - populateDeckPanel(gType); - - refreshPanels(true, true); - } - - /** Saves avatar prefs for players one and two. */ - private void updateAvatarPrefs() { - int pOneIndex = playerPanels.get(0).getAvatarIndex(); - int pTwoIndex = playerPanels.get(1).getAvatarIndex(); - - prefs.setPref(FPref.UI_AVATARS, pOneIndex + "," + pTwoIndex); - prefs.save(); - } - - /** Updates the avatars from preferences on update. */ - public void updatePlayersFromPrefs() { - ForgePreferences prefs = FModel.getPreferences(); - - // Avatar - String[] avatarPrefs = prefs.getPref(FPref.UI_AVATARS).split(","); - for (int i = 0; i < avatarPrefs.length; i++) { - int avatarIndex = Integer.parseInt(avatarPrefs[i]); - playerPanels.get(i).setAvatar(avatarIndex); - } - - // Name - String prefName = prefs.getPref(FPref.PLAYER_NAME); - playerPanels.get(0).setPlayerName(StringUtils.isBlank(prefName) ? "Human" : prefName); - } - - /** Adds a pre-styled FLabel component with the specified title. */ - private FLabel newLabel(String title) { - return new FLabel.Builder().text(title).fontSize(14).fontStyle(Font.ITALIC).build(); - } - - private List getUsedAvatars() { - List usedAvatars = Arrays.asList(-1,-1,-1,-1,-1,-1,-1,-1); - int i = 0; - for (PlayerPanel pp : playerPanels) { - usedAvatars.set(i++, pp.avatarIndex); - } - return usedAvatars; - } - - private final String getNewName() { - final String title = "Get new random name"; - final String message = "What type of name do you want to generate?"; - final SkinImage icon = FOptionPane.QUESTION_ICON; - final String[] genderOptions = new String[]{ "Male", "Female", "Any" }; - final String[] typeOptions = new String[]{ "Fantasy", "Generic", "Any" }; - - final int genderIndex = FOptionPane.showOptionDialog(message, title, icon, genderOptions, 2); - if (genderIndex < 0) { - return null; - } - final int typeIndex = FOptionPane.showOptionDialog(message, title, icon, typeOptions, 2); - if (typeIndex < 0) { - return null; - } - - final String gender = genderOptions[genderIndex]; - final String type = typeOptions[typeIndex]; - - String confirmMsg, newName; - List usedNames = getPlayerNames(); - do { - newName = NameGenerator.getRandomName(gender, type, usedNames); - confirmMsg = "Would you like to use the name \"" + newName + "\", or try again?"; - } while (!FOptionPane.showConfirmDialog(confirmMsg, title, "Use this name", "Try again", true)); - - return newName; - } - - private List getPlayerNames() { - List names = new ArrayList(); - for (PlayerPanel pp : playerPanels) { - names.add(pp.getPlayerName()); - } - return names; - } - - public String getPlayerName(int i) { - return playerPanels.get(i).getPlayerName(); - } - - public int getPlayerAvatar(int i) { - return playerPanels.get(i).getAvatarIndex(); - } - - public boolean isEnoughTeams() { - int lastTeam = -1; - final List teamList = appliedVariants.contains(GameType.Archenemy) ? archenemyTeams : teams; - - for (final int i : getParticipants()) { - if (lastTeam == -1) { - lastTeam = teamList.get(i); - } else if (lastTeam != teamList.get(i)) { - return true; - } - } - return false; - } - - ///////////////////////////////////////////// - //========== Various listeners in build order - - @SuppressWarnings("serial") - private class VariantCheckBox extends FCheckBox { - private final GameType variantType; - - private VariantCheckBox(GameType variantType0) { - super(variantType0.toString()); - - variantType = variantType0; - - setToolTipText(variantType.getDescription()); - - addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED) { - appliedVariants.add(variantType); - currentGameMode = variantType; - - //ensure other necessary variants are unchecked - switch (variantType) { - case Archenemy: - vntArchenemyRumble.setSelected(false); - break; - case ArchenemyRumble: - vntArchenemy.setSelected(false); - break; - case Commander: - vntTinyLeaders.setSelected(false); - vntMomirBasic.setSelected(false); - break; - case TinyLeaders: - vntCommander.setSelected(false); - vntMomirBasic.setSelected(false); - break; - case Vanguard: - vntMomirBasic.setSelected(false); - break; - case MomirBasic: - vntCommander.setSelected(false); - vntVanguard.setSelected(false); - break; - default: - break; - } - } - else { - appliedVariants.remove(variantType); - if (currentGameMode == variantType) { - currentGameMode = GameType.Constructed; - } - } - - for (PlayerPanel pp : playerPanels) { - pp.toggleIsPlayerArchenemy(); - } - changePlayerFocus(playerWithFocus, currentGameMode); - } - }); - } - } - - private ActionListener nameListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - FTextField nField = (FTextField)e.getSource(); - nField.transferFocus(); - } - }; - - /** This listener will look for a vanguard avatar being selected in the lists - / and update the corresponding detail panel. */ - private ListSelectionListener vgdLSListener = new ListSelectionListener() { - - @Override - public void valueChanged(ListSelectionEvent e) { - int index = vgdAvatarLists.indexOf(e.getSource()); - Object obj = vgdAvatarLists.get(index).getSelectedValue(); - PlayerPanel pp = playerPanels.get(index); - CardDetailPanel cdp = vgdAvatarDetails.get(index); - - if (obj instanceof PaperCard) { - pp.setVanguardButtonText(((PaperCard) obj).getName()); - cdp.setCard(CardView.getCardForUi((PaperCard) obj)); - cdp.setVisible(true); - refreshPanels(false, true); - } - else { - pp.setVanguardButtonText((String) obj); - cdp.setVisible(false); - } - } - }; - ///////////////////////////////////// //========== Overridden from IVDoc @@ -1327,100 +78,58 @@ public enum VSubmenuConstructed implements IVSubmenu { return parentCell; } - ///////////////////////////////////// - //========== METHODS FOR VARIANTS - - public Set getAppliedVariants() { - return appliedVariants; + /* (non-Javadoc) + * @see forge.gui.home.IVSubmenu#getGroupEnum() + */ + @Override + public EMenuGroup getGroupEnum() { + return EMenuGroup.SANCTIONED; } - public int getTeam(final int playerIndex) { - return appliedVariants.contains(GameType.Archenemy) ? archenemyTeams.get(playerIndex) : teams.get(playerIndex); + /* (non-Javadoc) + * @see forge.gui.home.IVSubmenu#getMenuTitle() + */ + @Override + public String getMenuTitle() { + return "Constructed"; } - /** Gets the list of planar deck lists. */ - public List> getPlanarDeckLists() { - return planarDeckLists; + /* (non-Javadoc) + * @see forge.gui.home.IVSubmenu#getItemEnum() + */ + @Override + public EDocID getItemEnum() { + return EDocID.HOME_CONSTRUCTED; } - /** Gets the list of commander deck lists. */ - public List> getCommanderDeckLists() { - return commanderDeckLists; - } + /* (non-Javadoc) + * @see forge.gui.home.IVSubmenu#populate() + */ + @Override + public void populate() { + final JPanel container = VHomeUI.SINGLETON_INSTANCE.getPnlDisplay(); - /** Gets the list of scheme deck lists. */ - public List> getSchemeDeckLists() { - return schemeDeckLists; - } + container.removeAll(); + container.setLayout(new MigLayout("insets 0, gap 0, wrap 1, ax right")); + container.add(lobby.getLblTitle(), "w 80%, h 40px!, gap 0 0 15px 15px, span 2, al right, pushx"); - public boolean isPlayerArchenemy(final int playernum) { - return playerPanels.get(playernum).playerIsArchenemy; - } - - /** Gets the list of Vanguard avatar lists. */ - public List> getVanguardLists() { - return vgdAvatarLists; - } - - /** Return all the Vanguard avatars. */ - public Iterable getAllAvatars() { - if (vgdAllAvatars.isEmpty()) { - for (PaperCard c : FModel.getMagicDb().getVariantCards().getAllCards()) { - if (c.getRules().getType().isVanguard()) { - vgdAllAvatars.add(c); + for (final FDeckChooser fdc : lobby.getDeckChoosers()) { + fdc.populate(); + fdc.getDecksComboBox().addListener(new IDecksComboBoxListener() { + @Override public final void deckTypeSelected(final DecksComboBoxEvent ev) { + lobby.getPlayerPanelWithFocus().focusOnAvatar(); } - } - } - return vgdAllAvatars; - } - - /** Return the Vanguard avatars not flagged RemAIDeck. */ - public List getAllAiAvatars() { - return vgdAllAiAvatars; - } - - /** Return the Vanguard avatars not flagged RemRandomDeck. */ - public List getNonRandomHumanAvatars() { - return nonRandomHumanAvatars; - } - - /** Return the Vanguard avatars not flagged RemAIDeck or RemRandomDeck. */ - public List getNonRandomAiAvatars() { - return nonRandomAiAvatars; - } - - /** Populate vanguard lists. */ - private void populateVanguardLists() { - humanListData.add("Use deck's default avatar (random if unavailable)"); - humanListData.add("Random"); - aiListData.add("Use deck's default avatar (random if unavailable)"); - aiListData.add("Random"); - for (PaperCard cp : getAllAvatars()) { - humanListData.add(cp); - if (!cp.getRules().getAiHints().getRemRandomDecks()) { - nonRandomHumanAvatars.add(cp); - } - if (!cp.getRules().getAiHints().getRemAIDecks()) { - aiListData.add(cp); - vgdAllAiAvatars.add(cp); - if (!cp.getRules().getAiHints().getRemRandomDecks()) { - nonRandomAiAvatars.add(cp); - } - } - } - } - - /** update vanguard list. */ - public void updateVanguardList(int playerIndex) { - FList vgdList = getVanguardLists().get(playerIndex); - Object lastSelection = vgdList.getSelectedValue(); - vgdList.setListData(isPlayerAI(playerIndex) ? aiListData : humanListData); - if (null != lastSelection) { - vgdList.setSelectedValue(lastSelection, true); + }); } - if (-1 == vgdList.getSelectedIndex()) { - vgdList.setSelectedIndex(0); + container.add(lobby.getConstructedFrame(), "gap 20px 20px 20px 0px, push, grow"); + container.add(lobby.getPanelStart(), "gap 0 0 3.5%! 3.5%!, ax center"); + + if (container.isShowing()) { + container.validate(); + container.repaint(); } + + lobby.changePlayerFocus(0); } } diff --git a/forge-gui-mobile/src/forge/GuiMobile.java b/forge-gui-mobile/src/forge/GuiMobile.java index 9c2f16f999b..f554a8d67b5 100644 --- a/forge-gui-mobile/src/forge/GuiMobile.java +++ b/forge-gui-mobile/src/forge/GuiMobile.java @@ -282,4 +282,8 @@ public class GuiMobile implements IGuiBase { public HostedMatch hostMatch() { return Forge.hostedMatch = new HostedMatch(); } + + @Override + public void netMessage(final String origin, final String message) { + } } diff --git a/forge-gui/src/main/java/forge/interfaces/IGuiBase.java b/forge-gui/src/main/java/forge/interfaces/IGuiBase.java index af1d1b28890..62be73c9365 100644 --- a/forge-gui/src/main/java/forge/interfaces/IGuiBase.java +++ b/forge-gui/src/main/java/forge/interfaces/IGuiBase.java @@ -51,4 +51,5 @@ public interface IGuiBase { void showBazaar(); IGuiGame getNewGuiGame(); HostedMatch hostMatch(); + void netMessage(String origin, String message); } \ No newline at end of file diff --git a/forge-gui/src/main/java/forge/match/NetGuiGame.java b/forge-gui/src/main/java/forge/match/NetGuiGame.java new file mode 100644 index 00000000000..721c2a10fdc --- /dev/null +++ b/forge-gui/src/main/java/forge/match/NetGuiGame.java @@ -0,0 +1,415 @@ +package forge.match; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.tuple.Pair; + +import com.google.common.base.Function; + +import forge.LobbyPlayer; +import forge.UiCommand; +import forge.assets.FSkinProp; +import forge.deck.CardPool; +import forge.game.GameEntity; +import forge.game.GameEntityView; +import forge.game.GameView; +import forge.game.card.CardView; +import forge.game.phase.PhaseType; +import forge.game.player.DelayedReveal; +import forge.game.player.IHasIcon; +import forge.game.player.PlayerView; +import forge.game.spellability.SpellAbility; +import forge.game.zone.ZoneType; +import forge.interfaces.IButton; +import forge.item.PaperCard; +import forge.net.game.GuiGameEvent; +import forge.net.game.server.IToClient; +import forge.player.PlayerControllerHuman; +import forge.trackable.TrackableObject; +import forge.util.FCollectionView; +import forge.util.ITriggerEvent; + +public class NetGuiGame extends AbstractGuiGame { + + private final IToClient client; + public NetGuiGame(final IToClient client) { + this.client = client; + } + + private void send(final String method) { + send(method, Collections.emptySet()); + } + private void send(final String method, final TrackableObject object) { + send(method, Collections.singleton(object)); + } + private void send(final String method, final Iterable objects) { + client.send(new GuiGameEvent(method, objects)); + } + + @Override + public void setGameView(final GameView gameView) { + super.setGameView(gameView); + send("setGameView", gameView); + } + + @Override + public boolean resetForNewGame() { + send("resetForNewGame"); + return true; + } + + @Override + public void openView(final Iterable myPlayers) { + send("openView", myPlayers); + } + + @Override + public void afterGameEnd() { + send("afterGameEnd"); + } + + @Override + public void showCombat() { + send("showCombat"); + } + + @Override + public void showPromptMessage(PlayerView playerView, String message) { + // TODO Auto-generated method stub + + } + + @Override + public boolean stopAtPhase(PlayerView playerTurn, PhaseType phase) { + // TODO Auto-generated method stub + return false; + } + + @Override + public IButton getBtnOK(final PlayerView playerView) { + return new NetButton(playerView, "OK"); + } + + @Override + public IButton getBtnCancel(final PlayerView playerView) { + return new NetButton(playerView, "Cancel"); + } + + @Override + public void focusButton(IButton button) { + // TODO Auto-generated method stub + + } + + @Override + public void flashIncorrectAction() { + // TODO Auto-generated method stub + + } + + @Override + public void updatePhase() { + // TODO Auto-generated method stub + + } + + @Override + public void updateTurn(PlayerView player) { + // TODO Auto-generated method stub + + } + + @Override + public void updatePlayerControl() { + // TODO Auto-generated method stub + + } + + @Override + public void enableOverlay() { + // TODO Auto-generated method stub + + } + + @Override + public void disableOverlay() { + // TODO Auto-generated method stub + + } + + @Override + public void finishGame() { + // TODO Auto-generated method stub + + } + + @Override + public Object showManaPool(PlayerView player) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void hideManaPool(PlayerView player, Object zoneToRestore) { + // TODO Auto-generated method stub + + } + + @Override + public void updateStack() { + // TODO Auto-generated method stub + + } + + @Override + public void updateZones(List> zonesToUpdate) { + // TODO Auto-generated method stub + + } + + @Override + public void updateSingleCard(CardView card) { + // TODO Auto-generated method stub + + } + + @Override + public void updateManaPool(Iterable manaPoolUpdate) { + // TODO Auto-generated method stub + + } + + @Override + public void updateLives(Iterable livesUpdate) { + // TODO Auto-generated method stub + + } + + @Override + public void setPanelSelection(CardView hostCard) { + // TODO Auto-generated method stub + + } + + @Override + public void hear(LobbyPlayer player, String message) { + // TODO Auto-generated method stub + + } + + @Override + public SpellAbility getAbilityToPlay(List abilities, + ITriggerEvent triggerEvent) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map assignDamage(CardView attacker, + List blockers, int damage, GameEntityView defender, + boolean overrideOrder) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void message(String message, String title) { + // TODO Auto-generated method stub + + } + + @Override + public void showErrorDialog(String message, String title) { + // TODO Auto-generated method stub + + } + + @Override + public boolean showConfirmDialog(String message, String title, + String yesButtonText, String noButtonText, boolean defaultYes) { + // TODO Auto-generated method stub + return false; + } + + @Override + public int showOptionDialog(String message, String title, FSkinProp icon, + String[] options, int defaultOption) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int showCardOptionDialog(CardView card, String message, + String title, FSkinProp icon, String[] options, int defaultOption) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public String showInputDialog(String message, String title, FSkinProp icon, + String initialInput, String[] inputOptions) { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean confirm(CardView c, String question, boolean defaultIsYes, + String[] options) { + // TODO Auto-generated method stub + return false; + } + + @Override + public List getChoices(String message, int min, int max, + Collection choices, T selected, Function display) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List order(String title, String top, int remainingObjectsMin, + int remainingObjectsMax, List sourceChoices, + List destChoices, CardView referenceCard, + boolean sideboardingMode) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List sideboard(CardPool sideboard, CardPool main) { + // TODO Auto-generated method stub + return null; + } + + @Override + public GameEntityView chooseSingleEntityForEffect(String title, + FCollectionView optionList, + DelayedReveal delayedReveal, boolean isOptional, + PlayerControllerHuman controller) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setCard(CardView card) { + // TODO Auto-generated method stub + + } + + @Override + public void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi) { + // TODO Auto-generated method stub + + } + + @Override + public boolean openZones(Collection zones, + Map players) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void restoreOldZones(Map playersToRestoreZonesFor) { + // TODO Auto-generated method stub + + } + + @Override + public boolean isUiSetToSkipPhase(PlayerView playerTurn, PhaseType phase) { + // TODO Auto-generated method stub + return false; + } + + @Override + protected void updateCurrentPlayer(PlayerView player) { + // TODO Auto-generated method stub + + } + + private final static class NetButton implements IButton { + + private final PlayerView playerView; + private final String button; + private NetButton(final PlayerView playerView, final String button) { + this.playerView = playerView; + this.button = button; + } + + @Override + public boolean isEnabled() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void setEnabled(boolean b0) { + // TODO Auto-generated method stub + + } + + @Override + public boolean isVisible() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void setVisible(boolean b0) { + // TODO Auto-generated method stub + + } + + @Override + public String getText() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setText(String text0) { + // TODO Auto-generated method stub + + } + + @Override + public boolean isSelected() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void setSelected(boolean b0) { + // TODO Auto-generated method stub + + } + + @Override + public boolean requestFocusInWindow() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void setCommand(UiCommand command0) { + // TODO Auto-generated method stub + + } + + @Override + public void setTextColor(FSkinProp color) { + // TODO Auto-generated method stub + + } + + @Override + public void setTextColor(int r, int g, int b) { + // TODO Auto-generated method stub + + } + + } +} diff --git a/forge-gui/src/main/java/forge/net/FGameClient.java b/forge-gui/src/main/java/forge/net/FGameClient.java new file mode 100644 index 00000000000..22a6d03e1a0 --- /dev/null +++ b/forge-gui/src/main/java/forge/net/FGameClient.java @@ -0,0 +1,124 @@ +package forge.net; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.serialization.ClassResolvers; +import io.netty.handler.codec.serialization.ObjectDecoder; +import io.netty.handler.codec.serialization.ObjectEncoder; +import forge.GuiBase; +import forge.game.GameView; +import forge.game.player.PlayerView; +import forge.interfaces.IGuiGame; +import forge.net.game.GuiGameEvent; +import forge.net.game.LoginEvent; +import forge.net.game.MessageEvent; +import forge.net.game.NetEvent; +import forge.net.game.client.IToServer; + +public class FGameClient implements IToServer { + private final IGuiGame clientGui; + public FGameClient(final String username, final String roomKey, final IGuiGame clientGui) { + this.clientGui = clientGui; + } + + static final int SIZE = Integer.parseInt(System.getProperty("size", "256")); + + private Channel channel; + public void connect(final String host, final int port) { + final EventLoopGroup group = new NioEventLoopGroup(); + try { + final Bootstrap b = new Bootstrap() + .group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + public void initChannel(final SocketChannel ch) throws Exception { + final ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast( + new ObjectEncoder(), + new ObjectDecoder(ClassResolvers.cacheDisabled(null)), + new MessageHandler(), + new GameClientHandler()); + } + }); + + // Start the connection attempt. + channel = b.connect(host, port).sync().channel(); + final ChannelFuture ch = channel.closeFuture(); + new Thread(new Runnable() { + @Override public void run() { + try { + ch.sync(); + } catch (final InterruptedException e) { + e.printStackTrace(); + } finally { + group.shutdownGracefully(); + } + } + }).start(); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + + public void send(final NetEvent event) { + channel.writeAndFlush(event); + } + + private class GameClientHandler extends ChannelInboundHandlerAdapter { + /** + * Creates a client-side handler. + */ + public GameClientHandler() { + } + + @Override + public void channelActive(final ChannelHandlerContext ctx) { + send(new LoginEvent("elcnesh")); + } + + @SuppressWarnings("unchecked") + @Override + public void channelRead(final ChannelHandlerContext ctx, final Object msg) { + System.out.println("Client received: " + msg); + if (msg instanceof GuiGameEvent) { + final GuiGameEvent event = (GuiGameEvent) msg; + switch (event.getMethod()) { + case "setGameView": + clientGui.setGameView((GameView) event.getObject()); + break; + case "openView": + clientGui.openView((Iterable) event.getObjects()); + default: + break; + } + } + } + + @Override + public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } + } + + private class MessageHandler extends ChannelInboundHandlerAdapter { + @Override + public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { + if (msg instanceof MessageEvent) { + final MessageEvent event = (MessageEvent) msg; + GuiBase.getInterface().netMessage(event.getSource(), event.getMessage()); + } + super.channelRead(ctx, msg); + } + } +} diff --git a/forge-gui/src/main/java/forge/net/FServerManager.java b/forge-gui/src/main/java/forge/net/FServerManager.java new file mode 100644 index 00000000000..f232a576804 --- /dev/null +++ b/forge-gui/src/main/java/forge/net/FServerManager.java @@ -0,0 +1,208 @@ +package forge.net; + +import forge.game.GameRules; +import forge.net.game.LoginEvent; +import forge.net.game.LogoutEvent; +import forge.net.game.MessageEvent; +import forge.net.game.NetEvent; +import forge.net.game.RegisterDeckEvent; +import forge.net.game.client.ILobbyListener; +import forge.net.game.server.RemoteClient; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.serialization.ClassResolvers; +import io.netty.handler.codec.serialization.ObjectDecoder; +import io.netty.handler.codec.serialization.ObjectEncoder; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; + +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +public final class FServerManager { + private static FServerManager instance = null; + + private final EventLoopGroup bossGroup = new NioEventLoopGroup(1); + private final EventLoopGroup workerGroup = new NioEventLoopGroup(); + private final Map games = Maps.newTreeMap(); + private int id = 0; + private final Map clients = Maps.newTreeMap(); + private final List lobbyListeners = Lists.newArrayListWithExpectedSize(1); + + private FServerManager() { + } + + private int nextId() { + return id++; + } + + public static FServerManager getInstance() { + if (instance == null) { + instance = new FServerManager(); + } + return instance; + } + + public void startServer(final int port) { + try { + final ServerBootstrap b = new ServerBootstrap() + .group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) throws Exception { + ChannelPipeline p = ch.pipeline(); + p.addLast( + new ObjectEncoder(), + new ObjectDecoder(ClassResolvers.cacheDisabled(null)), + new MessageHandler(), + new RegisterClientHandler(), + new ToLobbyListenersHandler(), + new DeregisterClientHandler(), + new GameServerHandler()); + } + }); + + // Bind and start to accept incoming connections. + final ChannelFuture ch = b.bind(port).sync().channel().closeFuture(); + new Thread(new Runnable() { + @Override public void run() { + try { + ch.sync(); + } catch (final InterruptedException e) { + e.printStackTrace(); + } finally { + stopServer(); + } + + } + }).start(); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + + public void stopServer() { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + + public void broadcast(final NetEvent event) { + for (final RemoteClient client : clients.values()) { + client.send(event); + } + } + + public void registerLobbyListener(final ILobbyListener lobbyListener) { + lobbyListeners.add(lobbyListener); + } + + public NetGame hostGame(final GameRules rules) { + final int id = nextId(); + final NetGame game = new NetGame(rules); + games.put(id, game); + return game; + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + stopServer(); + } + + private class MessageHandler extends ChannelInboundHandlerAdapter { + @Override public final void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { + final RemoteClient client = clients.get(ctx.channel()); + if (msg instanceof MessageEvent) { + broadcast(new MessageEvent(client.getUsername(), ((MessageEvent) msg).getMessage())); + } + super.channelRead(ctx, msg); + } + } + + private class GameServerHandler extends ChannelInboundHandlerAdapter { + @Override + public void channelRead(final ChannelHandlerContext ctx, final Object msg) { + System.out.println("Server received: " + msg); + } + + @Override + public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) { + cause.printStackTrace(); + ctx.close(); + } + } + + private class RegisterClientHandler extends ChannelInboundHandlerAdapter { + @Override + public void channelActive(final ChannelHandlerContext ctx) throws Exception { + final RemoteClient client = new RemoteClient(ctx.channel()); + clients.put(ctx.channel(), client); + games.get(0).addClient(client); + System.out.println("User connected to server at " + ctx.channel().remoteAddress()); + super.channelActive(ctx); + } + + @Override + public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { + final RemoteClient client = clients.get(ctx.channel()); + if (msg instanceof LoginEvent) { + client.setUsername(((LoginEvent) msg).getUsername()); + } else if (msg instanceof RegisterDeckEvent) { + games.get(0).registerDeck(client, ((RegisterDeckEvent) msg).getDeck()); + } + super.channelRead(ctx, msg); + } + } + + private class ToLobbyListenersHandler extends ChannelInboundHandlerAdapter { + @Override public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { + final RemoteClient client = clients.get(ctx.channel()); + if (msg instanceof LoginEvent) { + final LoginEvent event = (LoginEvent) msg; + for (final ILobbyListener lobbyListener : lobbyListeners) { + lobbyListener.login(client); + } + broadcast(event); + } else if (msg instanceof MessageEvent) { + final MessageEvent event = (MessageEvent) msg; + for (final ILobbyListener lobbyListener : lobbyListeners) { + lobbyListener.message(client.getUsername(), event.getMessage()); + } + broadcast(event); + } + super.channelRead(ctx, msg); + } + + @Override public void channelInactive(final ChannelHandlerContext ctx) throws Exception { + final RemoteClient client = clients.get(ctx.channel()); + for (final ILobbyListener lobbyListener : lobbyListeners) { + lobbyListener.logout(client); + } + super.channelInactive(ctx); + } + } + + private class DeregisterClientHandler extends ChannelInboundHandlerAdapter { + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + final RemoteClient client = clients.remove(ctx.channel()); + // TODO remove client from games + broadcast(new LogoutEvent(client.getUsername())); + super.channelInactive(ctx); + } + } +} diff --git a/forge-gui/src/main/java/forge/net/NetGame.java b/forge-gui/src/main/java/forge/net/NetGame.java new file mode 100644 index 00000000000..5748726db28 --- /dev/null +++ b/forge-gui/src/main/java/forge/net/NetGame.java @@ -0,0 +1,69 @@ +package forge.net; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import forge.ai.LobbyPlayerAi; +import forge.deck.Deck; +import forge.game.GameRules; +import forge.game.player.RegisteredPlayer; +import forge.interfaces.IGuiGame; +import forge.match.HostedMatch; +import forge.match.NetGuiGame; +import forge.net.game.server.RemoteClient; +import forge.player.LobbyPlayerHuman; + +public final class NetGame { + + private final Map clients = Maps.newHashMap(); + private final GameRules rules; + private final HostedMatch match = new HostedMatch(); + public NetGame(final GameRules rules) { + this.rules = rules; + } + + public void addClient(final RemoteClient client) { + clients.put(client, new NetPlayer(client, new NetGuiGame(client))); + } + + public void startMatch() { + final List registeredPlayers = Lists.newArrayListWithCapacity(clients.size()); + final Map guis = Maps.newHashMap(); + for (final NetPlayer np : clients.values()) { + if (np.player == null) { + System.err.println("No deck registered for player " + np.client.getUsername()); + return; + } + registeredPlayers.add(np.player); + guis.put(np.player, np.gui); + } + + // DEBUG + if (registeredPlayers.size() == 1) { + RegisteredPlayer r = new RegisteredPlayer(new Deck()); + registeredPlayers.add(r); + r.setPlayer(new LobbyPlayerAi("AI", new HashMap())); + } + match.startMatch(rules, null, registeredPlayers, guis); + } + + public void registerDeck(final RemoteClient client, final Deck deck) { + final RegisteredPlayer r = new RegisteredPlayer(deck); + clients.get(client).player = r; + r.setPlayer(new LobbyPlayerHuman(client.getUsername())); + } + + private static final class NetPlayer { + private final RemoteClient client; + private RegisteredPlayer player = null; + private final IGuiGame gui; + private NetPlayer(final RemoteClient client, final IGuiGame gui) { + this.client = client; + this.gui = gui; + } + } +} diff --git a/forge-gui/src/main/java/forge/net/package-info.java b/forge-gui/src/main/java/forge/net/package-info.java new file mode 100644 index 00000000000..3d560a403f2 --- /dev/null +++ b/forge-gui/src/main/java/forge/net/package-info.java @@ -0,0 +1 @@ +package forge.net; \ No newline at end of file diff --git a/forge-gui/src/main/java/forge/player/NetGameController.java b/forge-gui/src/main/java/forge/player/NetGameController.java new file mode 100644 index 00000000000..608239999b2 --- /dev/null +++ b/forge-gui/src/main/java/forge/player/NetGameController.java @@ -0,0 +1,128 @@ +package forge.player; + +import java.util.Collections; +import java.util.List; + +import forge.game.card.CardView; +import forge.game.player.PlayerView; +import forge.game.spellability.SpellAbility; +import forge.interfaces.IDevModeCheats; +import forge.interfaces.IGameController; +import forge.match.NextGameDecision; +import forge.net.game.client.IToServer; +import forge.trackable.TrackableObject; +import forge.util.ITriggerEvent; + +public class NetGameController implements IGameController { + + private final IToServer server; + public NetGameController(final IToServer server) { + this.server = server; + } + + private void send(final String method) { + send(method, Collections.emptySet()); + } + private void send(final String method, final TrackableObject object) { + send(method, Collections.singleton(object)); + } + private void send(final String method, final Iterable objects) { + //server.send(new (method, objects)); + } + + @Override + public boolean mayLookAtAllCards() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean canPlayUnlimitedLands() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void concede() { + send("concede"); + } + + @Override + public void alphaStrike() { + send("alphaStrike"); + } + + @Override + public boolean useMana(byte color) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void selectButtonOk() { + // TODO Auto-generated method stub + + } + + @Override + public void selectButtonCancel() { + // TODO Auto-generated method stub + + } + + @Override + public boolean passPriority() { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean passPriorityUntilEndOfTurn() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void selectPlayer(PlayerView playerView, ITriggerEvent triggerEvent) { + // TODO Auto-generated method stub + + } + + @Override + public boolean selectCard(CardView cardView, + List otherCardViewsToSelect, ITriggerEvent triggerEvent) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void selectAbility(SpellAbility sa) { + // TODO Auto-generated method stub + + } + + @Override + public boolean tryUndoLastAction() { + // TODO Auto-generated method stub + return false; + } + + @Override + public IDevModeCheats cheat() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void nextGameDecision(NextGameDecision decision) { + // TODO Auto-generated method stub + + } + + @Override + public String getActivateDescription(CardView card) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/forge-net/pom.xml b/forge-net/pom.xml index da80a7bef24..15941a8d9b1 100644 --- a/forge-net/pom.xml +++ b/forge-net/pom.xml @@ -28,7 +28,13 @@ gson 2.2.4 - + + io.netty + netty-all + 4.0.25.Final + compile + + org.eclipse.jetty jetty-websocket ${jettyVersion} diff --git a/forge-net/src/main/java/forge/net/client/GameServlet.java b/forge-net/src/main/java/forge/net/client/GameServlet.java new file mode 100644 index 00000000000..0ad7c897841 --- /dev/null +++ b/forge-net/src/main/java/forge/net/client/GameServlet.java @@ -0,0 +1,31 @@ +package forge.net.client; + +import javax.servlet.http.HttpServletRequest; + +import org.eclipse.jetty.websocket.WebSocket; +import org.eclipse.jetty.websocket.WebSocketServlet; + +import forge.net.IClientSocket; +import forge.net.NetServer; +import forge.net.NetServer.ClientSocket; + +public class GameServlet extends WebSocketServlet { + + private final String prefix; + private final ClientSocket socket; + public GameServlet(final NetServer netServer, final String prefix) { + this.prefix = prefix; + this.socket = netServer.new ClientSocket(); + } + + public IClientSocket getSocket() { + return socket; + } + + @Override + public WebSocket doWebSocketConnect(final HttpServletRequest request, final String protocol) { + System.out.printf("Connection from %s recieved%n", request.getRemoteAddr()); + return socket; + } + +} diff --git a/forge-net/src/main/java/forge/net/game/GuiGameEvent.java b/forge-net/src/main/java/forge/net/game/GuiGameEvent.java new file mode 100644 index 00000000000..d6cba6d9a73 --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/GuiGameEvent.java @@ -0,0 +1,29 @@ +package forge.net.game; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; + +import forge.trackable.TrackableObject; + +public final class GuiGameEvent implements NetEvent { + + private final String method; + private final Iterable objects; + + public GuiGameEvent(final String method, final Iterable objects) { + this.method = method; + this.objects = objects == null ? ImmutableSet.of() : objects; + } + + public String getMethod() { + return method; + } + + public TrackableObject getObject() { + return Iterables.getFirst(objects, null); + } + + public Iterable getObjects() { + return objects; + } +} diff --git a/forge-net/src/main/java/forge/net/game/IRemote.java b/forge-net/src/main/java/forge/net/game/IRemote.java new file mode 100644 index 00000000000..819e7e6f20b --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/IRemote.java @@ -0,0 +1,5 @@ +package forge.net.game; + +public interface IRemote { + void send(NetEvent event); +} diff --git a/forge-net/src/main/java/forge/net/game/LoginEvent.java b/forge-net/src/main/java/forge/net/game/LoginEvent.java new file mode 100644 index 00000000000..52e21bf488e --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/LoginEvent.java @@ -0,0 +1,14 @@ +package forge.net.game; + +public class LoginEvent implements NetEvent { + private static final long serialVersionUID = -8865183377417377938L; + + private final String username; + public LoginEvent(final String username) { + this.username = username; + } + + public String getUsername() { + return username; + } +} diff --git a/forge-net/src/main/java/forge/net/game/LogoutEvent.java b/forge-net/src/main/java/forge/net/game/LogoutEvent.java new file mode 100644 index 00000000000..be52cf36183 --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/LogoutEvent.java @@ -0,0 +1,14 @@ +package forge.net.game; + +public class LogoutEvent implements NetEvent { + private static final long serialVersionUID = -8262613254026625787L; + + private final String username; + public LogoutEvent(final String username) { + this.username = username; + } + + public String getUsername() { + return username; + } +} diff --git a/forge-net/src/main/java/forge/net/game/MessageEvent.java b/forge-net/src/main/java/forge/net/game/MessageEvent.java new file mode 100644 index 00000000000..080770c6123 --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/MessageEvent.java @@ -0,0 +1,23 @@ +package forge.net.game; + +public class MessageEvent implements NetEvent { + private static final long serialVersionUID = 1700060210647684186L; + + private final String source, message; + public MessageEvent(final String source, final String message) { + this.source = source; + this.message = message; + } + + public String getSource() { + return source; + } + public String getMessage() { + return message; + } + + @Override + public String toString() { + return getMessage(); + } +} diff --git a/forge-net/src/main/java/forge/net/game/NetEvent.java b/forge-net/src/main/java/forge/net/game/NetEvent.java new file mode 100644 index 00000000000..4a6acd4acc2 --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/NetEvent.java @@ -0,0 +1,6 @@ +package forge.net.game; + +import java.io.Serializable; + +public interface NetEvent extends Serializable { +} diff --git a/forge-net/src/main/java/forge/net/game/RegisterDeckEvent.java b/forge-net/src/main/java/forge/net/game/RegisterDeckEvent.java new file mode 100644 index 00000000000..3b3e2da248c --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/RegisterDeckEvent.java @@ -0,0 +1,16 @@ +package forge.net.game; + +import forge.deck.Deck; + +public class RegisterDeckEvent implements NetEvent { + private static final long serialVersionUID = -6553476654530937343L; + + private final Deck deck; + public RegisterDeckEvent(final Deck deck) { + this.deck = deck; + } + + public final Deck getDeck() { + return deck; + } +} diff --git a/forge-net/src/main/java/forge/net/game/client/ILobbyListener.java b/forge-net/src/main/java/forge/net/game/client/ILobbyListener.java new file mode 100644 index 00000000000..8443d061e3c --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/client/ILobbyListener.java @@ -0,0 +1,9 @@ +package forge.net.game.client; + +import forge.net.game.server.RemoteClient; + +public interface ILobbyListener { + void login(RemoteClient client); + void logout(RemoteClient client); + void message(String source, String message); +} diff --git a/forge-net/src/main/java/forge/net/game/client/IToServer.java b/forge-net/src/main/java/forge/net/game/client/IToServer.java new file mode 100644 index 00000000000..6346214fd32 --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/client/IToServer.java @@ -0,0 +1,6 @@ +package forge.net.game.client; + +import forge.net.game.IRemote; + +public interface IToServer extends IRemote { +} diff --git a/forge-net/src/main/java/forge/net/game/client/package-info.java b/forge-net/src/main/java/forge/net/game/client/package-info.java new file mode 100644 index 00000000000..a09dafa8d91 --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/client/package-info.java @@ -0,0 +1 @@ +package forge.net.game.client; \ No newline at end of file diff --git a/forge-net/src/main/java/forge/net/game/package-info.java b/forge-net/src/main/java/forge/net/game/package-info.java new file mode 100644 index 00000000000..0709d17a33b --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/package-info.java @@ -0,0 +1 @@ +package forge.net.game; \ No newline at end of file diff --git a/forge-net/src/main/java/forge/net/game/server/IToClient.java b/forge-net/src/main/java/forge/net/game/server/IToClient.java new file mode 100644 index 00000000000..8a9a789d458 --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/server/IToClient.java @@ -0,0 +1,6 @@ +package forge.net.game.server; + +import forge.net.game.IRemote; + +public interface IToClient extends IRemote { +} diff --git a/forge-net/src/main/java/forge/net/game/server/RemoteClient.java b/forge-net/src/main/java/forge/net/game/server/RemoteClient.java new file mode 100644 index 00000000000..3bee871f98d --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/server/RemoteClient.java @@ -0,0 +1,25 @@ +package forge.net.game.server; + +import forge.net.game.NetEvent; +import io.netty.channel.Channel; + +public final class RemoteClient implements IToClient { + + private final Channel channel; + private String username; + public RemoteClient(final Channel channel) { + this.channel = channel; + } + + @Override + public void send(final NetEvent event) { + channel.writeAndFlush(event); + } + + public String getUsername() { + return username; + } + public void setUsername(final String username) { + this.username = username; + } +} diff --git a/forge-net/src/main/java/forge/net/game/server/package-info.java b/forge-net/src/main/java/forge/net/game/server/package-info.java new file mode 100644 index 00000000000..06a8e74627f --- /dev/null +++ b/forge-net/src/main/java/forge/net/game/server/package-info.java @@ -0,0 +1 @@ +package forge.net.game.server; \ No newline at end of file