Update stability and fix issues in lobby and network games.

- Fix closing the lobby/client so that it notifies the clients/server.
- Improve deck selection in lobby (no longer adds Planes to deck and saves)
- Improve responsiveness of lobby over network
- In 2-player game over network, always assign local player to slot 0
- Warn when starting a commander deck without a commander
- Put poison counters next to life total (frees up space in the details panel)
This commit is contained in:
elcnesh
2015-04-02 08:56:31 +00:00
parent d3bcc089e3
commit 6bc69d0935
12 changed files with 235 additions and 138 deletions

View File

@@ -110,6 +110,7 @@ public enum FControl implements KeyEventDispatcher {
} }
} }
} }
/** /**
* <p> * <p>
* FControl. * FControl.

View File

@@ -239,7 +239,8 @@ public class VLobby implements IUpdateable {
isNewPanel = true; isNewPanel = true;
} }
panel.setType(slot.getType()); final LobbySlotType type = slot.getType();
panel.setType(type);
panel.setPlayerName(slot.getName()); panel.setPlayerName(slot.getName());
panel.setAvatar(slot.getAvatarIndex()); panel.setAvatar(slot.getAvatarIndex());
panel.setTeam(slot.getTeam()); panel.setTeam(slot.getTeam());
@@ -253,7 +254,7 @@ public class VLobby implements IUpdateable {
final FDeckChooser deckChooser = getDeckChooser(i); final FDeckChooser deckChooser = getDeckChooser(i);
deckChooser.setIsAi(slot.getType() == LobbySlotType.AI); deckChooser.setIsAi(slot.getType() == LobbySlotType.AI);
if (fullUpdate) { if (fullUpdate && (type == LobbySlotType.LOCAL || type == LobbySlotType.AI)) {
selectDeck(i); selectDeck(i);
} }
if (isNewPanel) { if (isNewPanel) {
@@ -298,7 +299,9 @@ public class VLobby implements IUpdateable {
} }
} }
private void fireDeckSectionChangeListener(final int index, final DeckSection section, final CardPool cards) { private void fireDeckSectionChangeListener(final int index, final DeckSection section, final CardPool cards) {
decks[index].putSection(section, cards); final Deck copy = new Deck(decks[index]);
copy.putSection(section, cards);
decks[index] = copy;
if (playerChangeListener != null) { if (playerChangeListener != null) {
playerChangeListener.update(index, UpdateLobbyPlayerEvent.deckUpdate(section, cards)); playerChangeListener.update(index, UpdateLobbyPlayerEvent.deckUpdate(section, cards));
} }
@@ -486,7 +489,7 @@ public class VLobby implements IUpdateable {
} }
private void selectPlanarDeck(final int playerIndex) { private void selectPlanarDeck(final int playerIndex) {
if (playerIndex >= activePlayersNum) { if (playerIndex >= activePlayersNum || !hasVariant(GameType.Planechase)) {
return; return;
} }

View File

@@ -8,6 +8,7 @@ import javax.swing.JMenu;
import forge.GuiBase; import forge.GuiBase;
import forge.Singletons; import forge.Singletons;
import forge.UiCommand; import forge.UiCommand;
import forge.assets.FSkinProp;
import forge.gui.FNetOverlay; import forge.gui.FNetOverlay;
import forge.gui.framework.FScreen; import forge.gui.framework.FScreen;
import forge.gui.framework.ICDoc; import forge.gui.framework.ICDoc;
@@ -31,6 +32,7 @@ import forge.net.game.UpdateLobbyPlayerEvent;
import forge.properties.ForgePreferences.FPref; import forge.properties.ForgePreferences.FPref;
import forge.screens.home.VLobby; import forge.screens.home.VLobby;
import forge.screens.home.sanctioned.ConstructedGameMenu; import forge.screens.home.sanctioned.ConstructedGameMenu;
import forge.util.gui.SOptionPane;
public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider { public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
SINGLETON_INSTANCE; SINGLETON_INSTANCE;
@@ -64,6 +66,9 @@ public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
@Override public final void message(final String source, final String message) { @Override public final void message(final String source, final String message) {
FNetOverlay.SINGLETON_INSTANCE.addMessage(source, message); FNetOverlay.SINGLETON_INSTANCE.addMessage(source, message);
} }
@Override public final void close() {
// NO-OP, server can't receive close message
}
}); });
FNetOverlay.SINGLETON_INSTANCE.setGameClient(new IRemote() { FNetOverlay.SINGLETON_INSTANCE.setGameClient(new IRemote() {
@Override public final void send(final NetEvent event) { @Override public final void send(final NetEvent event) {
@@ -79,6 +84,7 @@ public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
} }
}); });
view.populate();
view.update(true); view.update(true);
Singletons.getControl().setCurrentScreen(FScreen.ONLINE_LOBBY); Singletons.getControl().setCurrentScreen(FScreen.ONLINE_LOBBY);
@@ -88,6 +94,7 @@ public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
final void join(final String hostname, final int port) { final void join(final String hostname, final int port) {
final IGuiGame gui = GuiBase.getInterface().getNewGuiGame(); final IGuiGame gui = GuiBase.getInterface().getNewGuiGame();
final FGameClient client = new FGameClient(FModel.getPreferences().getPref(FPref.PLAYER_NAME), "0", gui); final FGameClient client = new FGameClient(FModel.getPreferences().getPref(FPref.PLAYER_NAME), "0", gui);
VOnlineLobby.SINGLETON_INSTANCE.setClient(client);
FNetOverlay.SINGLETON_INSTANCE.setGameClient(client); FNetOverlay.SINGLETON_INSTANCE.setGameClient(client);
final ClientGameLobby lobby = new ClientGameLobby(); final ClientGameLobby lobby = new ClientGameLobby();
final VLobby view = VOnlineLobby.SINGLETON_INSTANCE.setLobby(lobby); final VLobby view = VOnlineLobby.SINGLETON_INSTANCE.setLobby(lobby);
@@ -100,6 +107,11 @@ public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
lobby.setLocalPlayer(slot); lobby.setLocalPlayer(slot);
lobby.setData(state); lobby.setData(state);
} }
@Override public final void close() {
SOptionPane.showMessageDialog("Connection to the host was interrupted.", "Error", FSkinProp.ICO_WARNING);
VOnlineLobby.SINGLETON_INSTANCE.setClient(null);
FScreen.ONLINE_LOBBY.close();
}
}); });
view.setPlayerChangeListener(new IPlayerChangeListener() { view.setPlayerChangeListener(new IPlayerChangeListener() {
@Override public final void update(final int index, final UpdateLobbyPlayerEvent event) { @Override public final void update(final int index, final UpdateLobbyPlayerEvent event) {
@@ -109,7 +121,7 @@ public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
client.connect(hostname, port); client.connect(hostname, port);
Singletons.getControl().setCurrentScreen(FScreen.ONLINE_LOBBY); Singletons.getControl().setCurrentScreen(FScreen.ONLINE_LOBBY);
FNetOverlay.SINGLETON_INSTANCE.showUp(String.format("Connected to %s:%s", hostname, port)); FNetOverlay.SINGLETON_INSTANCE.showUp(String.format("Connected to %s:%d", hostname, port));
} }
@Override @Override

View File

@@ -6,6 +6,7 @@ import net.miginfocom.swing.MigLayout;
import forge.deckchooser.DecksComboBoxEvent; import forge.deckchooser.DecksComboBoxEvent;
import forge.deckchooser.FDeckChooser; import forge.deckchooser.FDeckChooser;
import forge.deckchooser.IDecksComboBoxListener; import forge.deckchooser.IDecksComboBoxListener;
import forge.gui.FNetOverlay;
import forge.gui.framework.DragCell; import forge.gui.framework.DragCell;
import forge.gui.framework.DragTab; import forge.gui.framework.DragTab;
import forge.gui.framework.EDocID; import forge.gui.framework.EDocID;
@@ -13,6 +14,8 @@ import forge.gui.framework.FScreen;
import forge.gui.framework.IVDoc; import forge.gui.framework.IVDoc;
import forge.gui.framework.IVTopLevelUI; import forge.gui.framework.IVTopLevelUI;
import forge.match.GameLobby; import forge.match.GameLobby;
import forge.net.FGameClient;
import forge.net.FServerManager;
import forge.screens.home.VLobby; import forge.screens.home.VLobby;
import forge.toolbox.FPanel; import forge.toolbox.FPanel;
import forge.util.gui.SOptionPane; import forge.util.gui.SOptionPane;
@@ -26,6 +29,7 @@ public enum VOnlineLobby implements IVDoc<COnlineLobby>, IVTopLevelUI {
// General variables // General variables
private VLobby lobby; private VLobby lobby;
private FGameClient client;
private VOnlineLobby() { private VOnlineLobby() {
} }
@@ -39,10 +43,15 @@ public enum VOnlineLobby implements IVDoc<COnlineLobby>, IVTopLevelUI {
return this.lobby; return this.lobby;
} }
void setClient(final FGameClient client) {
this.client = client;
}
@Override @Override
public void populate() { public void populate() {
final JPanel outerContainer = FView.SINGLETON_INSTANCE.getPnlInsets(); final JPanel outerContainer = FView.SINGLETON_INSTANCE.getPnlInsets();
outerContainer.removeAll(); outerContainer.removeAll();
final FPanel container = new FPanel(new MigLayout("insets 0, gap 0, wrap 1, ax right")); final FPanel container = new FPanel(new MigLayout("insets 0, gap 0, wrap 1, ax right"));
outerContainer.add(container); outerContainer.add(container);
lobby.getLblTitle().setText("Online Multiplayer: Lobby"); lobby.getLblTitle().setText("Online Multiplayer: Lobby");
@@ -106,7 +115,23 @@ public enum VOnlineLobby implements IVDoc<COnlineLobby>, IVTopLevelUI {
@Override @Override
public boolean onClosing(final FScreen screen) { public boolean onClosing(final FScreen screen) {
return SOptionPane.showConfirmDialog("Leave lobby?", "Leave"); final FServerManager server = FServerManager.getInstance();
if (server.isHosting()) {
if (SOptionPane.showConfirmDialog("Leave lobby? Doing so will shut down all connections and stop hosting.", "Leave")) {
FServerManager.getInstance().stopServer();
return true;
}
} else {
if (client == null || SOptionPane.showConfirmDialog("Leave lobby?", "Leave")) {
if (client != null) {
client.close();
client = null;
}
FNetOverlay.SINGLETON_INSTANCE.setGameClient(null);
return true;
}
}
return false;
} }
} }

View File

@@ -104,6 +104,7 @@ import forge.toolbox.MouseTriggerEvent;
import forge.toolbox.special.PhaseIndicator; import forge.toolbox.special.PhaseIndicator;
import forge.toolbox.special.PhaseLabel; import forge.toolbox.special.PhaseLabel;
import forge.trackable.TrackableCollection; import forge.trackable.TrackableCollection;
import forge.util.FCollection;
import forge.util.FCollectionView; import forge.util.FCollectionView;
import forge.util.ITriggerEvent; import forge.util.ITriggerEvent;
import forge.util.gui.SOptionPane; import forge.util.gui.SOptionPane;
@@ -260,11 +261,6 @@ public final class CMatchUI
return FSkin.getAvatars().get(avatarIdx >= 0 ? avatarIdx : defaultIndex); return FSkin.getAvatars().get(avatarIdx >= 0 ? avatarIdx : defaultIndex);
} }
private void setAvatar(VField view, SkinImage img) {
view.getLblAvatar().setIcon(img);
view.getLblAvatar().getResizeTimer().start();
}
private void initMatch(final FCollectionView<PlayerView> sortedPlayers, final Iterable<PlayerView> myPlayers) { private void initMatch(final FCollectionView<PlayerView> sortedPlayers, final Iterable<PlayerView> myPlayers) {
this.sortedPlayers = sortedPlayers; this.sortedPlayers = sortedPlayers;
this.setLocalPlayers(myPlayers); this.setLocalPlayers(myPlayers);
@@ -290,8 +286,7 @@ public final class CMatchUI
commands.add(c); commands.add(c);
myDocs.put(commandDoc, c); myDocs.put(commandDoc, c);
//setAvatar(f, new ImageIcon(FSkin.getAvatars().get())); f.setAvatar(getPlayerAvatar(p, Integer.parseInt(indices[i > 2 ? 1 : 0])));
setAvatar(f, getPlayerAvatar(p, Integer.parseInt(indices[i > 2 ? 1 : 0])));
f.getLayoutControl().initialize(); f.getLayoutControl().initialize();
c.getLayoutControl().initialize(); c.getLayoutControl().initialize();
i++; i++;
@@ -436,7 +431,7 @@ public final class CMatchUI
if (vHand != null) { if (vHand != null) {
vHand.getLayoutControl().updateHand(); vHand.getLayoutControl().updateHand();
} }
vf.getDetailsPanel().updateZones(); vf.updateZones();
vf.updateDetails(); vf.updateDetails();
FloatingCardArea.refresh(owner, zt); FloatingCardArea.refresh(owner, zt);
break; break;
@@ -448,7 +443,7 @@ public final class CMatchUI
break; break;
default: default:
if (vf != null) { if (vf != null) {
vf.getDetailsPanel().updateZones(); vf.updateZones();
} }
FloatingCardArea.refresh(owner, zt); FloatingCardArea.refresh(owner, zt);
break; break;
@@ -460,7 +455,7 @@ public final class CMatchUI
@Override @Override
public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) { public void updateManaPool(final Iterable<PlayerView> manaPoolUpdate) {
for (final PlayerView p : manaPoolUpdate) { for (final PlayerView p : manaPoolUpdate) {
getFieldViewFor(p).getDetailsPanel().updateManaPool(); getFieldViewFor(p).updateManaPool();
} }
} }
@@ -795,7 +790,12 @@ public final class CMatchUI
final GameView gameView = getGameView(); final GameView gameView = getGameView();
gameView.getGameLog().addObserver(getCLog()); gameView.getGameLog().addObserver(getCLog());
initMatch(gameView.getPlayers(), myPlayers); // Sort players
FCollectionView<PlayerView> players = gameView.getPlayers();
if (players.size() == 2 && myPlayers.size() == 1 && myPlayers.get(0).equals(players.get(1))) {
players = new FCollection<PlayerView>(new PlayerView[] { players.get(1), players.get(0) });
}
initMatch(players, myPlayers);
actuateMatchPreferences(); actuateMatchPreferences();

View File

@@ -28,6 +28,7 @@ import javax.swing.border.Border;
import javax.swing.border.LineBorder; import javax.swing.border.LineBorder;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import forge.assets.FSkinProp;
import forge.game.player.PlayerView; import forge.game.player.PlayerView;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.framework.DragCell; import forge.gui.framework.DragCell;
@@ -39,6 +40,7 @@ import forge.screens.match.controllers.CField;
import forge.toolbox.FLabel; import forge.toolbox.FLabel;
import forge.toolbox.FScrollPane; import forge.toolbox.FScrollPane;
import forge.toolbox.FSkin; import forge.toolbox.FSkin;
import forge.toolbox.FSkin.SkinImage;
import forge.toolbox.FSkin.SkinnedPanel; import forge.toolbox.FSkin.SkinnedPanel;
import forge.toolbox.special.PhaseIndicator; import forge.toolbox.special.PhaseIndicator;
import forge.toolbox.special.PlayerDetailsPanel; import forge.toolbox.special.PlayerDetailsPanel;
@@ -50,6 +52,9 @@ import forge.view.arcane.PlayArea;
* <br><br><i>(V at beginning of class name denotes a view class.)</i> * <br><br><i>(V at beginning of class name denotes a view class.)</i>
*/ */
public class VField implements IVDoc<CField> { public class VField implements IVDoc<CField> {
private final static int LIFE_CRITICAL = 5;
private final static int POISON_CRITICAL = 8;
// Fields used with interface IVDoc // Fields used with interface IVDoc
private final CField control; private final CField control;
private DragCell parentCell; private DragCell parentCell;
@@ -57,7 +62,7 @@ public class VField implements IVDoc<CField> {
private final DragTab tab = new DragTab("Field"); private final DragTab tab = new DragTab("Field");
// Other fields // Other fields
private PlayerView player = null; private final PlayerView player;
// Top-level containers // Top-level containers
private final FScrollPane scroller = new FScrollPane(false); private final FScrollPane scroller = new FScrollPane(false);
@@ -69,6 +74,7 @@ public class VField implements IVDoc<CField> {
// Avatar area // Avatar area
private final FLabel lblAvatar = new FLabel.Builder().fontAlign(SwingConstants.CENTER).iconScaleFactor(1.0f).build(); private final FLabel lblAvatar = new FLabel.Builder().fontAlign(SwingConstants.CENTER).iconScaleFactor(1.0f).build();
private final FLabel lblLife = new FLabel.Builder().fontAlign(SwingConstants.CENTER).fontStyle(Font.BOLD).build(); private final FLabel lblLife = new FLabel.Builder().fontAlign(SwingConstants.CENTER).fontStyle(Font.BOLD).build();
private final FLabel lblPoison = new FLabel.Builder().fontAlign(SwingConstants.CENTER).fontStyle(Font.BOLD).icon(FSkin.getImage(FSkinProp.IMG_ZONE_POISON)).iconInBackground().build();
private final PhaseIndicator phaseIndicator = new PhaseIndicator(); private final PhaseIndicator phaseIndicator = new PhaseIndicator();
@@ -100,12 +106,13 @@ public class VField implements IVDoc<CField> {
lblAvatar.setFocusable(false); lblAvatar.setFocusable(false);
lblLife.setFocusable(false); lblLife.setFocusable(false);
lblPoison.setFocusable(false);
avatarArea.setOpaque(false); avatarArea.setOpaque(false);
avatarArea.setBackground(FSkin.getColor(FSkin.Colors.CLR_HOVER)); avatarArea.setBackground(FSkin.getColor(FSkin.Colors.CLR_HOVER));
avatarArea.setLayout(new MigLayout("insets 0, gap 0")); avatarArea.setLayout(new MigLayout("insets 0, gap 0"));
avatarArea.add(lblAvatar, "w 100%-6px!, h 100%-23px!, wrap, gap 3 3 3 0"); avatarArea.add(lblAvatar, "w 100%-6px!, h 100%-23px!, wrap, gap 3 3 3 0");
avatarArea.add(lblLife, "w 100%!, h 20px!"); avatarArea.add(lblLife, "w 100%!, h 20px!, wrap");
// Player area hover effect // Player area hover effect
avatarArea.addMouseListener(new MouseAdapter() { avatarArea.addMouseListener(new MouseAdapter() {
@@ -170,10 +177,6 @@ public class VField implements IVDoc<CField> {
return this.parentCell; return this.parentCell;
} }
public PlayerView getPlayer() {
return this.player;
}
public PlayArea getTabletop() { public PlayArea getTabletop() {
return this.tabletop; return this.tabletop;
} }
@@ -182,14 +185,6 @@ public class VField implements IVDoc<CField> {
return this.avatarArea; return this.avatarArea;
} }
public FLabel getLblAvatar() {
return this.lblAvatar;
}
public FLabel getLblLife() {
return this.lblLife;
}
public PhaseIndicator getPhaseIndicator() { public PhaseIndicator getPhaseIndicator() {
return phaseIndicator; return phaseIndicator;
} }
@@ -198,23 +193,67 @@ public class VField implements IVDoc<CField> {
return detailsPanel; return detailsPanel;
} }
public boolean isHighlighted() { private boolean isHighlighted() {
return control.getMatchUI().isHighlighted(player); return control.getMatchUI().isHighlighted(player);
} }
public void setAvatar(final SkinImage avatar) {
lblAvatar.setIcon(avatar);
lblAvatar.getResizeTimer().start();
}
public void updateManaPool() {
detailsPanel.updateManaPool();
}
public void updateZones() {
detailsPanel.updateZones();
}
private void addLblPoison() {
if (lblPoison.isShowing()) {
return;
}
avatarArea.remove(lblLife);
avatarArea.add(lblLife, "w 50%!, h 20px!, split 2");
avatarArea.add(lblPoison, "w 50%!, h 20px!, wrap");
}
private void removeLblPoison() {
if (!lblPoison.isShowing()) {
return;
}
avatarArea.remove(lblPoison);
avatarArea.remove(lblLife);
avatarArea.add(lblLife, "w 100%!, h 20px!, wrap");
}
public void updateDetails() { public void updateDetails() {
control.getMatchUI().getCPlayers().update(); control.getMatchUI().getCPlayers().update();
detailsPanel.updateDetails(); detailsPanel.updateDetails();
this.getLblLife().setText("" + player.getLife()); // Update life total
if (player.getLife() > 5) { final int life = player.getLife();
this.getLblLife().setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT)); lblLife.setText(String.valueOf(life));
} if (life > LIFE_CRITICAL) {
else { lblLife.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
this.getLblLife().setForeground(Color.red); } else {
lblLife.setForeground(Color.RED);
} }
boolean highlighted = isHighlighted(); // Update poison counters
final int poison = player.getPoisonCounters();
if (poison > 0) {
addLblPoison();
lblPoison.setText(String.valueOf(poison));
if (poison < POISON_CRITICAL) {
lblPoison.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
} else {
lblPoison.setForeground(Color.RED);
}
} else {
removeLblPoison();
}
final boolean highlighted = isHighlighted();
this.avatarArea.setBorder(highlighted ? borderAvatarHighlighted : borderAvatarSimple ); this.avatarArea.setBorder(highlighted ? borderAvatarHighlighted : borderAvatarSimple );
this.avatarArea.setOpaque(highlighted); this.avatarArea.setOpaque(highlighted);
this.avatarArea.setToolTipText(player.getDetailsHtml()); this.avatarArea.setToolTipText(player.getDetailsHtml());

View File

@@ -1,6 +1,7 @@
package forge.toolbox.special; package forge.toolbox.special;
import java.awt.Color; import java.awt.Color;
import java.awt.Component;
import java.awt.Font; import java.awt.Font;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
@@ -33,23 +34,23 @@ public class PlayerDetailsPanel extends JPanel {
private final PlayerView player; private final PlayerView player;
// Info labels // Info labels
private final FLabel lblHand = new DetailLabel(FSkinProp.IMG_ZONE_HAND, "99", "Cards in hand"); private final DetailLabel lblHand = new DetailLabel(FSkinProp.IMG_ZONE_HAND, "Hand (%s/%s)");
private final FLabel lblGraveyard = new DetailLabel(FSkinProp.IMG_ZONE_GRAVEYARD, "99", "Cards in graveyard"); private final DetailLabel lblGraveyard = new DetailLabel(FSkinProp.IMG_ZONE_GRAVEYARD, "Graveyard (%s)");
private final FLabel lblLibrary = new DetailLabel(FSkinProp.IMG_ZONE_LIBRARY, "99", "Cards in library"); private final DetailLabel lblLibrary = new DetailLabel(FSkinProp.IMG_ZONE_LIBRARY, "Library (%s)");
private final FLabel lblExile = new DetailLabel(FSkinProp.IMG_ZONE_EXILE, "99", "Exiled cards"); private final DetailLabel lblExile = new DetailLabel(FSkinProp.IMG_ZONE_EXILE, "Exile (%s)");
private final FLabel lblFlashback = new DetailLabel(FSkinProp.IMG_ZONE_FLASHBACK, "99", "Flashback cards"); private final DetailLabel lblFlashback = new DetailLabel(FSkinProp.IMG_ZONE_FLASHBACK, "Flashback cards (%s)");
private final FLabel lblPoison = new DetailLabel(FSkinProp.IMG_ZONE_POISON, "99", "Poison counters"); private final DetailLabel lblPoison = new DetailLabel(FSkinProp.IMG_ZONE_POISON, "Poison counters (%s)");
private final List<Pair<DetailLabel, Byte>> manaLabels = new ArrayList<Pair<DetailLabel, Byte>>(); private final List<Pair<DetailLabel, Byte>> manaLabels = new ArrayList<Pair<DetailLabel, Byte>>();
public PlayerDetailsPanel(final PlayerView player0) { public PlayerDetailsPanel(final PlayerView player0) {
player = player0; player = player0;
manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_B, "99", "Black mana"), MagicColor.BLACK)); manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_W, "White mana (%s)"), MagicColor.WHITE));
manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_U, "99", "Blue mana"), MagicColor.BLUE)); manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_U, "Blue mana (%s)"), MagicColor.BLUE));
manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_G, "99", "Green mana"), MagicColor.GREEN)); manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_B, "Black mana (%s)"), MagicColor.BLACK));
manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_R, "99", "Red mana"), MagicColor.RED)); manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_R, "Red mana (%s)"), MagicColor.RED));
manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_W, "99", "White mana"), MagicColor.WHITE)); manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_G, "Green mana (%s)"), MagicColor.GREEN));
manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_COLORLESS, "99", "Colorless mana"), (byte)0)); manaLabels.add(Pair.of(new DetailLabel(FSkinProp.IMG_MANA_COLORLESS, "Colorless mana (%s)"), MagicColor.COLORLESS));
setOpaque(false); setOpaque(false);
setLayout(new MigLayout("insets 0, gap 0, wrap")); setLayout(new MigLayout("insets 0, gap 0, wrap"));
@@ -105,74 +106,59 @@ public class PlayerDetailsPanel extends JPanel {
add(row6, constraintsRow); add(row6, constraintsRow);
} }
public Component getLblLibrary() {
return lblLibrary;
}
/** /**
* Handles observer update of player Zones - hand, graveyard, etc. * Handles observer update of player Zones - hand, graveyard, etc.
* *
* @param p0 &emsp; {@link forge.game.player.Player} * @param p0 &emsp; {@link forge.game.player.Player}
*/ */
public void updateZones() { public void updateZones() {
getLblHand().setText("" + player.getHandSize()); final String handSize = String.valueOf(player.getHandSize()),
final String handMaxToolTip = player.hasUnlimitedHandSize() graveyardSize = String.valueOf(player.getGraveyardSize()),
? "no maximum hand size" : String.valueOf(player.getMaxHandSize()); librarySize = String.valueOf(player.getLibrarySize()),
getLblHand().setToolTipText("Cards in hand (max: " + handMaxToolTip + ")"); flashbackSize = String.valueOf(player.getFlashbackSize()),
getLblGraveyard().setText("" + player.getGraveyardSize()); exileSize = String.valueOf(player.getExileSize());
getLblLibrary().setText("" + player.getLibrarySize()); lblHand.setText(handSize);
getLblFlashback().setText("" + player.getFlashbackSize()); lblHand.setToolTip(handSize, player.getMaxHandString());
getLblExile().setText("" + player.getExileSize()); lblGraveyard.setText(graveyardSize);
lblGraveyard.setToolTip(graveyardSize);
lblLibrary.setText(librarySize);
lblLibrary.setToolTip(librarySize);
lblFlashback.setText(flashbackSize);
lblFlashback.setToolTip(flashbackSize);
lblExile.setText(exileSize);
lblExile.setToolTip(exileSize);
} }
/** /**
* Handles observer update of non-Zone details - life, poison, etc. Also * Handles observer update of non-Zone details (poison).
* updates "players" panel in tabber for this player.
*
* @param p0 &emsp; {@link forge.game.player.Player}
*/ */
public void updateDetails() { public void updateDetails() {
// Poison/life // Poison
getLblPoison().setText("" + player.getPoisonCounters()); final int poison = player.getPoisonCounters();
if (player.getPoisonCounters() < 8) { lblPoison.setText(String.valueOf(poison));
getLblPoison().setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT)); lblPoison.setToolTip(String.valueOf(poison));
} if (poison < 8) {
else { lblPoison.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
getLblPoison().setForeground(Color.red); } else {
lblPoison.setForeground(Color.red);
} }
} }
/** /**
* Handles observer update of the mana pool. * Handles observer update of the mana pool.
*
* @param p0 &emsp; {@link forge.game.player.Player}
*/ */
public void updateManaPool() { public void updateManaPool() {
for (final Pair<DetailLabel, Byte> label : manaLabels) { for (final Pair<DetailLabel, Byte> label : manaLabels) {
label.getKey().setText(Integer.toString(player.getMana(label.getRight()))); final String mana = String.valueOf(player.getMana(label.getRight().byteValue()));
label.getKey().setText(mana);
label.getKey().setToolTip(mana);
} }
} }
public FLabel getLblHand() {
return lblHand;
}
public FLabel getLblLibrary() {
return lblLibrary;
}
public FLabel getLblGraveyard() {
return lblGraveyard;
}
public FLabel getLblExile() {
return lblExile;
}
public FLabel getLblFlashback() {
return lblFlashback;
}
public FLabel getLblPoison() {
return lblPoison;
}
public void setupMouseActions(final ForgeAction handAction, final ForgeAction libraryAction, final ForgeAction exileAction, public void setupMouseActions(final ForgeAction handAction, final ForgeAction libraryAction, final ForgeAction exileAction,
final ForgeAction graveAction, final ForgeAction flashBackAction, final Function<Byte, Boolean> manaAction) { final ForgeAction graveAction, final ForgeAction flashBackAction, final Function<Byte, Boolean> manaAction) {
// Detail label listeners // Detail label listeners
@@ -212,7 +198,8 @@ public class PlayerDetailsPanel extends JPanel {
@Override @Override
public void onLeftClick(final MouseEvent e) { public void onLeftClick(final MouseEvent e) {
//if shift key down, keep using mana until it runs out or no longer can be put towards the cost //if shift key down, keep using mana until it runs out or no longer can be put towards the cost
while (manaAction.apply(labelPair.getRight()) && e.isShiftDown()) {} final Byte mana = labelPair.getRight();
while (manaAction.apply(mana) && e.isShiftDown()) {}
} }
}); });
} }
@@ -220,33 +207,39 @@ public class PlayerDetailsPanel extends JPanel {
@SuppressWarnings("serial") @SuppressWarnings("serial")
private class DetailLabel extends FLabel { private class DetailLabel extends FLabel {
private DetailLabel(FSkinProp p0, String s0, String s1) { private final String tooltip;
super(new FLabel.Builder().icon(FSkin.getImage(p0)) private DetailLabel(final FSkinProp icon, final String tooltip) {
super(new FLabel.Builder().icon(FSkin.getImage(icon))
.opaque(false).fontSize(14).hoverable() .opaque(false).fontSize(14).hoverable()
.fontStyle(Font.BOLD).iconInBackground() .fontStyle(Font.BOLD).iconInBackground()
.text(s0).tooltip(s1).fontAlign(SwingConstants.RIGHT)); .fontAlign(SwingConstants.RIGHT));
this.tooltip = tooltip;
setFocusable(false); setFocusable(false);
} }
public void setText(String text0) { public void setText(final String text0) {
super.setText(text0); super.setText(text0);
autoSizeFont(); autoSizeFont();
} }
public void setToolTip(final String... args) {
super.setToolTipText(String.format(tooltip, (Object[]) args));
}
protected void resetIcon() { protected void resetIcon() {
super.resetIcon(); super.resetIcon();
autoSizeFont(); autoSizeFont();
} }
private void autoSizeFont() { private void autoSizeFont() {
String text = getText(); final String text = getText();
if (StringUtils.isEmpty(text)) { return; } if (StringUtils.isEmpty(text)) { return; }
Graphics g = getGraphics(); final Graphics g = getGraphics();
if (g == null) { return; } if (g == null) { return; }
int max = getMaxTextWidth(); final int max = getMaxTextWidth();
SkinFont font = null; SkinFont font = null;
for (int fontSize = 14; fontSize > 5; fontSize--) { for (int fontSize = 14; fontSize > 5; fontSize--) {

View File

@@ -5,4 +5,5 @@ import forge.match.GameLobby.GameLobbyData;
public interface ILobbyListener { public interface ILobbyListener {
void message(String source, String message); void message(String source, String message);
void update(GameLobbyData state, int slot); void update(GameLobbyData state, int slot);
void close();
} }

View File

@@ -41,6 +41,7 @@ public abstract class GameLobby {
private final static int MAX_PLAYERS = 8; private final static int MAX_PLAYERS = 8;
private GameLobbyData data = new GameLobbyData(); private GameLobbyData data = new GameLobbyData();
private GameType currentGameType = GameType.Constructed;
private int lastArchenemy = 0; private int lastArchenemy = 0;
private IUpdateable listener; private IUpdateable listener;
@@ -69,10 +70,10 @@ public abstract class GameLobby {
} }
public GameType getGameType() { public GameType getGameType() {
return data.currentGameMode; return currentGameType;
} }
public void setGameType(final GameType type) { public void setGameType(final GameType type) {
data.currentGameMode = type; currentGameType = type;
} }
public boolean hasVariant(final GameType variant) { public boolean hasVariant(final GameType variant) {
@@ -90,7 +91,7 @@ public abstract class GameLobby {
} }
public void applyToSlot(final int index, final UpdateLobbyPlayerEvent event) { public void applyToSlot(final int index, final UpdateLobbyPlayerEvent event) {
final LobbySlot slot = getSlot(index); final LobbySlot slot = getSlot(index);
if (slot == null) { if (slot == null || event == null) {
throw new NullPointerException(); throw new NullPointerException();
} }
@@ -130,10 +131,10 @@ public abstract class GameLobby {
} }
public abstract boolean hasControl(); public abstract boolean hasControl();
public abstract boolean mayEdit(final int index); public abstract boolean mayEdit(int index);
public abstract boolean mayControl(final int index); public abstract boolean mayControl(int index);
public abstract boolean mayRemove(final int index); public abstract boolean mayRemove(int index);
protected abstract IGuiGame getGui(final int index); protected abstract IGuiGame getGui(int index);
protected abstract void onGameStarted(); protected abstract void onGameStarted();
public void addSlot() { public void addSlot() {
@@ -142,6 +143,9 @@ public abstract class GameLobby {
addSlot(new LobbySlot(type, null, newIndex, newIndex, false, !allowNetworking, Collections.<AIOption>emptySet())); addSlot(new LobbySlot(type, null, newIndex, newIndex, false, !allowNetworking, Collections.<AIOption>emptySet()));
} }
protected final void addSlot(final LobbySlot slot) { protected final void addSlot(final LobbySlot slot) {
if (slot == null) {
throw new NullPointerException();
}
if (data.slots.size() >= MAX_PLAYERS) { if (data.slots.size() >= MAX_PLAYERS) {
return; return;
} }
@@ -197,7 +201,7 @@ public abstract class GameLobby {
} }
public void applyVariant(final GameType variant) { public void applyVariant(final GameType variant) {
data.currentGameMode = variant; setGameType(variant);
data.appliedVariants.add(variant); data.appliedVariants.add(variant);
//ensure other necessary variants are unchecked //ensure other necessary variants are unchecked
@@ -231,17 +235,21 @@ public abstract class GameLobby {
} }
public void removeVariant(final GameType variant) { public void removeVariant(final GameType variant) {
data.appliedVariants.remove(variant); final boolean changed = data.appliedVariants.remove(variant);
if (!changed) {
return;
}
if (data.appliedVariants.isEmpty()) { if (data.appliedVariants.isEmpty()) {
data.appliedVariants.add(GameType.Constructed); data.appliedVariants.add(GameType.Constructed);
} }
if (variant == data.currentGameMode) { if (variant == currentGameType) {
if (hasVariant(GameType.Commander)) { if (hasVariant(GameType.Commander)) {
data.currentGameMode = GameType.Commander; currentGameType = GameType.Commander;
} else if (hasVariant(GameType.TinyLeaders)) { } else if (hasVariant(GameType.TinyLeaders)) {
data.currentGameMode = GameType.TinyLeaders; currentGameType = GameType.TinyLeaders;
} else { } else {
data.currentGameMode = GameType.Constructed; currentGameType = GameType.Constructed;
} }
} }
updateView(true); updateView(true);
@@ -275,7 +283,7 @@ public abstract class GameLobby {
} }
final List<LobbySlot> activeSlots = Lists.newArrayListWithCapacity(getNumberOfSlots()); final List<LobbySlot> activeSlots = Lists.newArrayListWithCapacity(getNumberOfSlots());
for (final LobbySlot slot : data.getSlots()) { for (final LobbySlot slot : data.slots) {
if (slot.getType() != LobbySlotType.OPEN) { if (slot.getType() != LobbySlotType.OPEN) {
activeSlots.add(slot); activeSlots.add(slot);
} }
@@ -290,6 +298,12 @@ public abstract class GameLobby {
SOptionPane.showMessageDialog(String.format("Please specify a deck for %s", slot.getName())); SOptionPane.showMessageDialog(String.format("Please specify a deck for %s", slot.getName()));
return; return;
} }
if (hasVariant(GameType.Commander) || hasVariant(GameType.TinyLeaders)) {
if (!slot.getDeck().has(DeckSection.Commander)) {
SOptionPane.showMessageDialog(String.format("%s doesn't have a commander", slot.getName()));
return;
}
}
} }
final Set<GameType> variantTypes = data.appliedVariants; final Set<GameType> variantTypes = data.appliedVariants;
@@ -391,7 +405,7 @@ public abstract class GameLobby {
return; return;
} }
} }
schemes = schemePool.toFlatList(); schemes = schemePool == null ? Collections.<PaperCard>emptyList() : schemePool.toFlatList();
} }
//Planechase //Planechase
@@ -404,7 +418,7 @@ public abstract class GameLobby {
return; return;
} }
} }
planes = planePool.toFlatList(); planes = planePool == null ? Collections.<PaperCard>emptyList() : planePool.toFlatList();
} }
//Vanguard //Vanguard
@@ -441,23 +455,12 @@ public abstract class GameLobby {
} }
public final static class GameLobbyData implements Serializable { public final static class GameLobbyData implements Serializable {
private static final long serialVersionUID = -9038854736144187540L; private static final long serialVersionUID = 9184758307999646864L;
private transient GameType currentGameMode = GameType.Constructed;
private final Set<GameType> appliedVariants = EnumSet.of(GameType.Constructed); private final Set<GameType> appliedVariants = EnumSet.of(GameType.Constructed);
private final List<LobbySlot> slots = Lists.newArrayList(); private final List<LobbySlot> slots = Lists.newArrayList();
public GameType getCurrentGameMode() { public GameLobbyData() {
return currentGameMode;
}
public void setCurrentGameMode(GameType currentGameMode) {
this.currentGameMode = currentGameMode;
}
public Set<GameType> getAppliedVariants() {
return Collections.unmodifiableSet(appliedVariants);
}
public List<LobbySlot> getSlots() {
return Collections.unmodifiableList(slots);
} }
} }
} }

View File

@@ -62,10 +62,11 @@ public final class LobbySlot implements Serializable {
setAiOptions(data.getAiOptions()); setAiOptions(data.getAiOptions());
changed = true; changed = true;
} }
final Deck oldDeck = getDeck();
if (data.getDeck() != null) { if (data.getDeck() != null) {
setDeck(data.getDeck()); setDeck(data.getDeck());
} else if (getDeck() != null && data.getSection() != null && data.getCards() != null) { } else if (oldDeck != null && data.getSection() != null && data.getCards() != null) {
getDeck().putSection(data.getSection(), data.getCards()); oldDeck.putSection(data.getSection(), data.getCards());
} }
return changed; return changed;
} }

View File

@@ -104,6 +104,10 @@ public class FGameClient implements IToServer {
} }
} }
public void close() {
channel.close();
}
@Override @Override
public void send(final NetEvent event) { public void send(final NetEvent event) {
System.out.println("Client sent " + event); System.out.println("Client sent " + event);
@@ -382,5 +386,13 @@ public class FGameClient implements IToServer {
} }
super.channelRead(ctx, msg); super.channelRead(ctx, msg);
} }
@Override
public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
for (final ILobbyListener listener : lobbyListeners) {
listener.close();
}
super.channelInactive(ctx);
}
} }
} }

View File

@@ -52,6 +52,7 @@ import com.google.common.collect.Maps;
public final class FServerManager { public final class FServerManager {
private static FServerManager instance = null; private static FServerManager instance = null;
private boolean isHosting = false;
private final EventLoopGroup bossGroup = new NioEventLoopGroup(1); private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
private final EventLoopGroup workerGroup = new NioEventLoopGroup(); private final EventLoopGroup workerGroup = new NioEventLoopGroup();
private final Map<Channel, RemoteClient> clients = Maps.newTreeMap(); private final Map<Channel, RemoteClient> clients = Maps.newTreeMap();
@@ -102,6 +103,7 @@ public final class FServerManager {
} }
}).start(); }).start();
isHosting = true;
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -110,6 +112,11 @@ public final class FServerManager {
public void stopServer() { public void stopServer() {
bossGroup.shutdownGracefully(); bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully(); workerGroup.shutdownGracefully();
isHosting = false;
}
public boolean isHosting() {
return isHosting;
} }
public void broadcast(final NetEvent event) { public void broadcast(final NetEvent event) {