mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 19:28:01 +00:00
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:
@@ -110,6 +110,7 @@ public enum FControl implements KeyEventDispatcher {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* FControl.
|
||||
|
||||
@@ -239,7 +239,8 @@ public class VLobby implements IUpdateable {
|
||||
isNewPanel = true;
|
||||
}
|
||||
|
||||
panel.setType(slot.getType());
|
||||
final LobbySlotType type = slot.getType();
|
||||
panel.setType(type);
|
||||
panel.setPlayerName(slot.getName());
|
||||
panel.setAvatar(slot.getAvatarIndex());
|
||||
panel.setTeam(slot.getTeam());
|
||||
@@ -253,7 +254,7 @@ public class VLobby implements IUpdateable {
|
||||
|
||||
final FDeckChooser deckChooser = getDeckChooser(i);
|
||||
deckChooser.setIsAi(slot.getType() == LobbySlotType.AI);
|
||||
if (fullUpdate) {
|
||||
if (fullUpdate && (type == LobbySlotType.LOCAL || type == LobbySlotType.AI)) {
|
||||
selectDeck(i);
|
||||
}
|
||||
if (isNewPanel) {
|
||||
@@ -298,7 +299,9 @@ public class VLobby implements IUpdateable {
|
||||
}
|
||||
}
|
||||
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) {
|
||||
playerChangeListener.update(index, UpdateLobbyPlayerEvent.deckUpdate(section, cards));
|
||||
}
|
||||
@@ -486,7 +489,7 @@ public class VLobby implements IUpdateable {
|
||||
}
|
||||
|
||||
private void selectPlanarDeck(final int playerIndex) {
|
||||
if (playerIndex >= activePlayersNum) {
|
||||
if (playerIndex >= activePlayersNum || !hasVariant(GameType.Planechase)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import javax.swing.JMenu;
|
||||
import forge.GuiBase;
|
||||
import forge.Singletons;
|
||||
import forge.UiCommand;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.gui.FNetOverlay;
|
||||
import forge.gui.framework.FScreen;
|
||||
import forge.gui.framework.ICDoc;
|
||||
@@ -31,6 +32,7 @@ import forge.net.game.UpdateLobbyPlayerEvent;
|
||||
import forge.properties.ForgePreferences.FPref;
|
||||
import forge.screens.home.VLobby;
|
||||
import forge.screens.home.sanctioned.ConstructedGameMenu;
|
||||
import forge.util.gui.SOptionPane;
|
||||
|
||||
public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
|
||||
SINGLETON_INSTANCE;
|
||||
@@ -64,6 +66,9 @@ public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
|
||||
@Override public final void message(final String source, final String 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() {
|
||||
@Override public final void send(final NetEvent event) {
|
||||
@@ -79,6 +84,7 @@ public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
|
||||
}
|
||||
});
|
||||
|
||||
view.populate();
|
||||
view.update(true);
|
||||
|
||||
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 IGuiGame gui = GuiBase.getInterface().getNewGuiGame();
|
||||
final FGameClient client = new FGameClient(FModel.getPreferences().getPref(FPref.PLAYER_NAME), "0", gui);
|
||||
VOnlineLobby.SINGLETON_INSTANCE.setClient(client);
|
||||
FNetOverlay.SINGLETON_INSTANCE.setGameClient(client);
|
||||
final ClientGameLobby lobby = new ClientGameLobby();
|
||||
final VLobby view = VOnlineLobby.SINGLETON_INSTANCE.setLobby(lobby);
|
||||
@@ -100,6 +107,11 @@ public enum CSubmenuOnlineLobby implements ICDoc, IMenuProvider {
|
||||
lobby.setLocalPlayer(slot);
|
||||
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() {
|
||||
@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);
|
||||
|
||||
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
|
||||
|
||||
@@ -6,6 +6,7 @@ import net.miginfocom.swing.MigLayout;
|
||||
import forge.deckchooser.DecksComboBoxEvent;
|
||||
import forge.deckchooser.FDeckChooser;
|
||||
import forge.deckchooser.IDecksComboBoxListener;
|
||||
import forge.gui.FNetOverlay;
|
||||
import forge.gui.framework.DragCell;
|
||||
import forge.gui.framework.DragTab;
|
||||
import forge.gui.framework.EDocID;
|
||||
@@ -13,6 +14,8 @@ import forge.gui.framework.FScreen;
|
||||
import forge.gui.framework.IVDoc;
|
||||
import forge.gui.framework.IVTopLevelUI;
|
||||
import forge.match.GameLobby;
|
||||
import forge.net.FGameClient;
|
||||
import forge.net.FServerManager;
|
||||
import forge.screens.home.VLobby;
|
||||
import forge.toolbox.FPanel;
|
||||
import forge.util.gui.SOptionPane;
|
||||
@@ -26,6 +29,7 @@ public enum VOnlineLobby implements IVDoc<COnlineLobby>, IVTopLevelUI {
|
||||
|
||||
// General variables
|
||||
private VLobby lobby;
|
||||
private FGameClient client;
|
||||
|
||||
private VOnlineLobby() {
|
||||
}
|
||||
@@ -39,10 +43,15 @@ public enum VOnlineLobby implements IVDoc<COnlineLobby>, IVTopLevelUI {
|
||||
return this.lobby;
|
||||
}
|
||||
|
||||
void setClient(final FGameClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@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");
|
||||
@@ -106,7 +115,23 @@ public enum VOnlineLobby implements IVDoc<COnlineLobby>, IVTopLevelUI {
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ import forge.toolbox.MouseTriggerEvent;
|
||||
import forge.toolbox.special.PhaseIndicator;
|
||||
import forge.toolbox.special.PhaseLabel;
|
||||
import forge.trackable.TrackableCollection;
|
||||
import forge.util.FCollection;
|
||||
import forge.util.FCollectionView;
|
||||
import forge.util.ITriggerEvent;
|
||||
import forge.util.gui.SOptionPane;
|
||||
@@ -260,11 +261,6 @@ public final class CMatchUI
|
||||
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) {
|
||||
this.sortedPlayers = sortedPlayers;
|
||||
this.setLocalPlayers(myPlayers);
|
||||
@@ -290,8 +286,7 @@ public final class CMatchUI
|
||||
commands.add(c);
|
||||
myDocs.put(commandDoc, c);
|
||||
|
||||
//setAvatar(f, new ImageIcon(FSkin.getAvatars().get()));
|
||||
setAvatar(f, getPlayerAvatar(p, Integer.parseInt(indices[i > 2 ? 1 : 0])));
|
||||
f.setAvatar(getPlayerAvatar(p, Integer.parseInt(indices[i > 2 ? 1 : 0])));
|
||||
f.getLayoutControl().initialize();
|
||||
c.getLayoutControl().initialize();
|
||||
i++;
|
||||
@@ -436,7 +431,7 @@ public final class CMatchUI
|
||||
if (vHand != null) {
|
||||
vHand.getLayoutControl().updateHand();
|
||||
}
|
||||
vf.getDetailsPanel().updateZones();
|
||||
vf.updateZones();
|
||||
vf.updateDetails();
|
||||
FloatingCardArea.refresh(owner, zt);
|
||||
break;
|
||||
@@ -448,7 +443,7 @@ public final class CMatchUI
|
||||
break;
|
||||
default:
|
||||
if (vf != null) {
|
||||
vf.getDetailsPanel().updateZones();
|
||||
vf.updateZones();
|
||||
}
|
||||
FloatingCardArea.refresh(owner, zt);
|
||||
break;
|
||||
@@ -460,7 +455,7 @@ public final class CMatchUI
|
||||
@Override
|
||||
public void updateManaPool(final Iterable<PlayerView> 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();
|
||||
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();
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import javax.swing.border.Border;
|
||||
import javax.swing.border.LineBorder;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import forge.assets.FSkinProp;
|
||||
import forge.game.player.PlayerView;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.gui.framework.DragCell;
|
||||
@@ -39,6 +40,7 @@ import forge.screens.match.controllers.CField;
|
||||
import forge.toolbox.FLabel;
|
||||
import forge.toolbox.FScrollPane;
|
||||
import forge.toolbox.FSkin;
|
||||
import forge.toolbox.FSkin.SkinImage;
|
||||
import forge.toolbox.FSkin.SkinnedPanel;
|
||||
import forge.toolbox.special.PhaseIndicator;
|
||||
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>
|
||||
*/
|
||||
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
|
||||
private final CField control;
|
||||
private DragCell parentCell;
|
||||
@@ -57,7 +62,7 @@ public class VField implements IVDoc<CField> {
|
||||
private final DragTab tab = new DragTab("Field");
|
||||
|
||||
// Other fields
|
||||
private PlayerView player = null;
|
||||
private final PlayerView player;
|
||||
|
||||
// Top-level containers
|
||||
private final FScrollPane scroller = new FScrollPane(false);
|
||||
@@ -68,7 +73,8 @@ public class VField implements IVDoc<CField> {
|
||||
|
||||
// Avatar area
|
||||
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();
|
||||
|
||||
@@ -100,12 +106,13 @@ public class VField implements IVDoc<CField> {
|
||||
|
||||
lblAvatar.setFocusable(false);
|
||||
lblLife.setFocusable(false);
|
||||
lblPoison.setFocusable(false);
|
||||
|
||||
avatarArea.setOpaque(false);
|
||||
avatarArea.setBackground(FSkin.getColor(FSkin.Colors.CLR_HOVER));
|
||||
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(lblLife, "w 100%!, h 20px!");
|
||||
avatarArea.add(lblLife, "w 100%!, h 20px!, wrap");
|
||||
|
||||
// Player area hover effect
|
||||
avatarArea.addMouseListener(new MouseAdapter() {
|
||||
@@ -170,10 +177,6 @@ public class VField implements IVDoc<CField> {
|
||||
return this.parentCell;
|
||||
}
|
||||
|
||||
public PlayerView getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
public PlayArea getTabletop() {
|
||||
return this.tabletop;
|
||||
}
|
||||
@@ -182,14 +185,6 @@ public class VField implements IVDoc<CField> {
|
||||
return this.avatarArea;
|
||||
}
|
||||
|
||||
public FLabel getLblAvatar() {
|
||||
return this.lblAvatar;
|
||||
}
|
||||
|
||||
public FLabel getLblLife() {
|
||||
return this.lblLife;
|
||||
}
|
||||
|
||||
public PhaseIndicator getPhaseIndicator() {
|
||||
return phaseIndicator;
|
||||
}
|
||||
@@ -198,23 +193,67 @@ public class VField implements IVDoc<CField> {
|
||||
return detailsPanel;
|
||||
}
|
||||
|
||||
public boolean isHighlighted() {
|
||||
private boolean isHighlighted() {
|
||||
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() {
|
||||
control.getMatchUI().getCPlayers().update();
|
||||
detailsPanel.updateDetails();
|
||||
|
||||
this.getLblLife().setText("" + player.getLife());
|
||||
if (player.getLife() > 5) {
|
||||
this.getLblLife().setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
|
||||
}
|
||||
else {
|
||||
this.getLblLife().setForeground(Color.red);
|
||||
// Update life total
|
||||
final int life = player.getLife();
|
||||
lblLife.setText(String.valueOf(life));
|
||||
if (life > LIFE_CRITICAL) {
|
||||
lblLife.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
|
||||
} 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.setOpaque(highlighted);
|
||||
this.avatarArea.setToolTipText(player.getDetailsHtml());
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package forge.toolbox.special;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.MouseEvent;
|
||||
@@ -33,23 +34,23 @@ public class PlayerDetailsPanel extends JPanel {
|
||||
private final PlayerView player;
|
||||
|
||||
// Info labels
|
||||
private final FLabel lblHand = new DetailLabel(FSkinProp.IMG_ZONE_HAND, "99", "Cards in hand");
|
||||
private final FLabel lblGraveyard = new DetailLabel(FSkinProp.IMG_ZONE_GRAVEYARD, "99", "Cards in graveyard");
|
||||
private final FLabel lblLibrary = new DetailLabel(FSkinProp.IMG_ZONE_LIBRARY, "99", "Cards in library");
|
||||
private final FLabel lblExile = new DetailLabel(FSkinProp.IMG_ZONE_EXILE, "99", "Exiled cards");
|
||||
private final FLabel lblFlashback = new DetailLabel(FSkinProp.IMG_ZONE_FLASHBACK, "99", "Flashback cards");
|
||||
private final FLabel lblPoison = new DetailLabel(FSkinProp.IMG_ZONE_POISON, "99", "Poison counters");
|
||||
private final DetailLabel lblHand = new DetailLabel(FSkinProp.IMG_ZONE_HAND, "Hand (%s/%s)");
|
||||
private final DetailLabel lblGraveyard = new DetailLabel(FSkinProp.IMG_ZONE_GRAVEYARD, "Graveyard (%s)");
|
||||
private final DetailLabel lblLibrary = new DetailLabel(FSkinProp.IMG_ZONE_LIBRARY, "Library (%s)");
|
||||
private final DetailLabel lblExile = new DetailLabel(FSkinProp.IMG_ZONE_EXILE, "Exile (%s)");
|
||||
private final DetailLabel lblFlashback = new DetailLabel(FSkinProp.IMG_ZONE_FLASHBACK, "Flashback cards (%s)");
|
||||
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>>();
|
||||
|
||||
public PlayerDetailsPanel(final PlayerView 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_U, "99", "Blue mana"), 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_R, "99", "Red mana"), 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_COLORLESS, "99", "Colorless mana"), (byte)0));
|
||||
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, "Blue mana (%s)"), MagicColor.BLUE));
|
||||
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, "Red mana (%s)"), MagicColor.RED));
|
||||
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, "Colorless mana (%s)"), MagicColor.COLORLESS));
|
||||
|
||||
setOpaque(false);
|
||||
setLayout(new MigLayout("insets 0, gap 0, wrap"));
|
||||
@@ -105,74 +106,59 @@ public class PlayerDetailsPanel extends JPanel {
|
||||
add(row6, constraintsRow);
|
||||
}
|
||||
|
||||
public Component getLblLibrary() {
|
||||
return lblLibrary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles observer update of player Zones - hand, graveyard, etc.
|
||||
*
|
||||
* @param p0   {@link forge.game.player.Player}
|
||||
*/
|
||||
public void updateZones() {
|
||||
getLblHand().setText("" + player.getHandSize());
|
||||
final String handMaxToolTip = player.hasUnlimitedHandSize()
|
||||
? "no maximum hand size" : String.valueOf(player.getMaxHandSize());
|
||||
getLblHand().setToolTipText("Cards in hand (max: " + handMaxToolTip + ")");
|
||||
getLblGraveyard().setText("" + player.getGraveyardSize());
|
||||
getLblLibrary().setText("" + player.getLibrarySize());
|
||||
getLblFlashback().setText("" + player.getFlashbackSize());
|
||||
getLblExile().setText("" + player.getExileSize());
|
||||
final String handSize = String.valueOf(player.getHandSize()),
|
||||
graveyardSize = String.valueOf(player.getGraveyardSize()),
|
||||
librarySize = String.valueOf(player.getLibrarySize()),
|
||||
flashbackSize = String.valueOf(player.getFlashbackSize()),
|
||||
exileSize = String.valueOf(player.getExileSize());
|
||||
lblHand.setText(handSize);
|
||||
lblHand.setToolTip(handSize, player.getMaxHandString());
|
||||
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
|
||||
* updates "players" panel in tabber for this player.
|
||||
*
|
||||
* @param p0   {@link forge.game.player.Player}
|
||||
* Handles observer update of non-Zone details (poison).
|
||||
*/
|
||||
public void updateDetails() {
|
||||
// Poison/life
|
||||
getLblPoison().setText("" + player.getPoisonCounters());
|
||||
if (player.getPoisonCounters() < 8) {
|
||||
getLblPoison().setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
|
||||
}
|
||||
else {
|
||||
getLblPoison().setForeground(Color.red);
|
||||
// Poison
|
||||
final int poison = player.getPoisonCounters();
|
||||
lblPoison.setText(String.valueOf(poison));
|
||||
lblPoison.setToolTip(String.valueOf(poison));
|
||||
if (poison < 8) {
|
||||
lblPoison.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT));
|
||||
} else {
|
||||
lblPoison.setForeground(Color.red);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles observer update of the mana pool.
|
||||
*
|
||||
* @param p0   {@link forge.game.player.Player}
|
||||
*/
|
||||
public void updateManaPool() {
|
||||
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,
|
||||
final ForgeAction graveAction, final ForgeAction flashBackAction, final Function<Byte, Boolean> manaAction) {
|
||||
// Detail label listeners
|
||||
@@ -212,7 +198,8 @@ public class PlayerDetailsPanel extends JPanel {
|
||||
@Override
|
||||
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
|
||||
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")
|
||||
private class DetailLabel extends FLabel {
|
||||
private DetailLabel(FSkinProp p0, String s0, String s1) {
|
||||
super(new FLabel.Builder().icon(FSkin.getImage(p0))
|
||||
private final String tooltip;
|
||||
private DetailLabel(final FSkinProp icon, final String tooltip) {
|
||||
super(new FLabel.Builder().icon(FSkin.getImage(icon))
|
||||
.opaque(false).fontSize(14).hoverable()
|
||||
.fontStyle(Font.BOLD).iconInBackground()
|
||||
.text(s0).tooltip(s1).fontAlign(SwingConstants.RIGHT));
|
||||
.fontAlign(SwingConstants.RIGHT));
|
||||
|
||||
this.tooltip = tooltip;
|
||||
setFocusable(false);
|
||||
}
|
||||
|
||||
public void setText(String text0) {
|
||||
public void setText(final String text0) {
|
||||
super.setText(text0);
|
||||
autoSizeFont();
|
||||
}
|
||||
|
||||
public void setToolTip(final String... args) {
|
||||
super.setToolTipText(String.format(tooltip, (Object[]) args));
|
||||
}
|
||||
|
||||
protected void resetIcon() {
|
||||
super.resetIcon();
|
||||
autoSizeFont();
|
||||
}
|
||||
|
||||
private void autoSizeFont() {
|
||||
String text = getText();
|
||||
final String text = getText();
|
||||
if (StringUtils.isEmpty(text)) { return; }
|
||||
|
||||
Graphics g = getGraphics();
|
||||
final Graphics g = getGraphics();
|
||||
if (g == null) { return; }
|
||||
|
||||
int max = getMaxTextWidth();
|
||||
final int max = getMaxTextWidth();
|
||||
|
||||
SkinFont font = null;
|
||||
for (int fontSize = 14; fontSize > 5; fontSize--) {
|
||||
|
||||
@@ -5,4 +5,5 @@ import forge.match.GameLobby.GameLobbyData;
|
||||
public interface ILobbyListener {
|
||||
void message(String source, String message);
|
||||
void update(GameLobbyData state, int slot);
|
||||
void close();
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ public abstract class GameLobby {
|
||||
private final static int MAX_PLAYERS = 8;
|
||||
|
||||
private GameLobbyData data = new GameLobbyData();
|
||||
private GameType currentGameType = GameType.Constructed;
|
||||
private int lastArchenemy = 0;
|
||||
|
||||
private IUpdateable listener;
|
||||
@@ -69,10 +70,10 @@ public abstract class GameLobby {
|
||||
}
|
||||
|
||||
public GameType getGameType() {
|
||||
return data.currentGameMode;
|
||||
return currentGameType;
|
||||
}
|
||||
public void setGameType(final GameType type) {
|
||||
data.currentGameMode = type;
|
||||
currentGameType = type;
|
||||
}
|
||||
|
||||
public boolean hasVariant(final GameType variant) {
|
||||
@@ -90,7 +91,7 @@ public abstract class GameLobby {
|
||||
}
|
||||
public void applyToSlot(final int index, final UpdateLobbyPlayerEvent event) {
|
||||
final LobbySlot slot = getSlot(index);
|
||||
if (slot == null) {
|
||||
if (slot == null || event == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
@@ -130,10 +131,10 @@ public abstract class GameLobby {
|
||||
}
|
||||
|
||||
public abstract boolean hasControl();
|
||||
public abstract boolean mayEdit(final int index);
|
||||
public abstract boolean mayControl(final int index);
|
||||
public abstract boolean mayRemove(final int index);
|
||||
protected abstract IGuiGame getGui(final int index);
|
||||
public abstract boolean mayEdit(int index);
|
||||
public abstract boolean mayControl(int index);
|
||||
public abstract boolean mayRemove(int index);
|
||||
protected abstract IGuiGame getGui(int index);
|
||||
protected abstract void onGameStarted();
|
||||
|
||||
public void addSlot() {
|
||||
@@ -142,6 +143,9 @@ public abstract class GameLobby {
|
||||
addSlot(new LobbySlot(type, null, newIndex, newIndex, false, !allowNetworking, Collections.<AIOption>emptySet()));
|
||||
}
|
||||
protected final void addSlot(final LobbySlot slot) {
|
||||
if (slot == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (data.slots.size() >= MAX_PLAYERS) {
|
||||
return;
|
||||
}
|
||||
@@ -197,7 +201,7 @@ public abstract class GameLobby {
|
||||
}
|
||||
|
||||
public void applyVariant(final GameType variant) {
|
||||
data.currentGameMode = variant;
|
||||
setGameType(variant);
|
||||
data.appliedVariants.add(variant);
|
||||
|
||||
//ensure other necessary variants are unchecked
|
||||
@@ -231,17 +235,21 @@ public abstract class GameLobby {
|
||||
}
|
||||
|
||||
public void removeVariant(final GameType variant) {
|
||||
data.appliedVariants.remove(variant);
|
||||
final boolean changed = data.appliedVariants.remove(variant);
|
||||
if (!changed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.appliedVariants.isEmpty()) {
|
||||
data.appliedVariants.add(GameType.Constructed);
|
||||
}
|
||||
if (variant == data.currentGameMode) {
|
||||
if (variant == currentGameType) {
|
||||
if (hasVariant(GameType.Commander)) {
|
||||
data.currentGameMode = GameType.Commander;
|
||||
currentGameType = GameType.Commander;
|
||||
} else if (hasVariant(GameType.TinyLeaders)) {
|
||||
data.currentGameMode = GameType.TinyLeaders;
|
||||
currentGameType = GameType.TinyLeaders;
|
||||
} else {
|
||||
data.currentGameMode = GameType.Constructed;
|
||||
currentGameType = GameType.Constructed;
|
||||
}
|
||||
}
|
||||
updateView(true);
|
||||
@@ -275,7 +283,7 @@ public abstract class GameLobby {
|
||||
}
|
||||
|
||||
final List<LobbySlot> activeSlots = Lists.newArrayListWithCapacity(getNumberOfSlots());
|
||||
for (final LobbySlot slot : data.getSlots()) {
|
||||
for (final LobbySlot slot : data.slots) {
|
||||
if (slot.getType() != LobbySlotType.OPEN) {
|
||||
activeSlots.add(slot);
|
||||
}
|
||||
@@ -290,6 +298,12 @@ public abstract class GameLobby {
|
||||
SOptionPane.showMessageDialog(String.format("Please specify a deck for %s", slot.getName()));
|
||||
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;
|
||||
@@ -391,7 +405,7 @@ public abstract class GameLobby {
|
||||
return;
|
||||
}
|
||||
}
|
||||
schemes = schemePool.toFlatList();
|
||||
schemes = schemePool == null ? Collections.<PaperCard>emptyList() : schemePool.toFlatList();
|
||||
}
|
||||
|
||||
//Planechase
|
||||
@@ -404,7 +418,7 @@ public abstract class GameLobby {
|
||||
return;
|
||||
}
|
||||
}
|
||||
planes = planePool.toFlatList();
|
||||
planes = planePool == null ? Collections.<PaperCard>emptyList() : planePool.toFlatList();
|
||||
}
|
||||
|
||||
//Vanguard
|
||||
@@ -441,23 +455,12 @@ public abstract class GameLobby {
|
||||
}
|
||||
|
||||
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 List<LobbySlot> slots = Lists.newArrayList();
|
||||
|
||||
public GameType getCurrentGameMode() {
|
||||
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);
|
||||
public GameLobbyData() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,10 +62,11 @@ public final class LobbySlot implements Serializable {
|
||||
setAiOptions(data.getAiOptions());
|
||||
changed = true;
|
||||
}
|
||||
final Deck oldDeck = getDeck();
|
||||
if (data.getDeck() != null) {
|
||||
setDeck(data.getDeck());
|
||||
} else if (getDeck() != null && data.getSection() != null && data.getCards() != null) {
|
||||
getDeck().putSection(data.getSection(), data.getCards());
|
||||
} else if (oldDeck != null && data.getSection() != null && data.getCards() != null) {
|
||||
oldDeck.putSection(data.getSection(), data.getCards());
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
@@ -104,6 +104,10 @@ public class FGameClient implements IToServer {
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
channel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(final NetEvent event) {
|
||||
System.out.println("Client sent " + event);
|
||||
@@ -382,5 +386,13 @@ public class FGameClient implements IToServer {
|
||||
}
|
||||
super.channelRead(ctx, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
|
||||
for (final ILobbyListener listener : lobbyListeners) {
|
||||
listener.close();
|
||||
}
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import com.google.common.collect.Maps;
|
||||
public final class FServerManager {
|
||||
private static FServerManager instance = null;
|
||||
|
||||
private boolean isHosting = false;
|
||||
private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
|
||||
private final EventLoopGroup workerGroup = new NioEventLoopGroup();
|
||||
private final Map<Channel, RemoteClient> clients = Maps.newTreeMap();
|
||||
@@ -102,6 +103,7 @@ public final class FServerManager {
|
||||
|
||||
}
|
||||
}).start();
|
||||
isHosting = true;
|
||||
} catch (final InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -110,6 +112,11 @@ public final class FServerManager {
|
||||
public void stopServer() {
|
||||
bossGroup.shutdownGracefully();
|
||||
workerGroup.shutdownGracefully();
|
||||
isHosting = false;
|
||||
}
|
||||
|
||||
public boolean isHosting() {
|
||||
return isHosting;
|
||||
}
|
||||
|
||||
public void broadcast(final NetEvent event) {
|
||||
|
||||
Reference in New Issue
Block a user