* GUI: added popup menu to view player's outside/sideboard at any time (allows to view only own or computer's sideboards);

This commit is contained in:
Oleg Agafonov 2021-07-21 13:44:35 +04:00
parent eda50cc7b1
commit 28473c7bd0
12 changed files with 265 additions and 165 deletions

View file

@ -34,7 +34,7 @@ public class CardInfoWindowDialog extends MageDialog {
private static final Logger LOGGER = Logger.getLogger(CardInfoWindowDialog.class);
public enum ShowType {
REVEAL, REVEAL_TOP_LIBRARY, LOOKED_AT, EXILE, GRAVEYARD, COMPANION, OTHER
REVEAL, REVEAL_TOP_LIBRARY, LOOKED_AT, EXILE, GRAVEYARD, COMPANION, SIDEBOARD, OTHER
}
private final ShowType showType;
@ -89,6 +89,17 @@ public class CardInfoWindowDialog extends MageDialog {
}
});
break;
case SIDEBOARD:
this.setFrameIcon(new ImageIcon(ImageHelper.getImageFromResources("/info/library.png")));
this.setClosable(true);
this.setDefaultCloseOperation(HIDE_ON_CLOSE);
this.addInternalFrameListener(new InternalFrameAdapter() {
@Override
public void internalFrameClosing(InternalFrameEvent e) {
CardInfoWindowDialog.this.hideDialog();
}
});
break;
case EXILE:
this.setFrameIcon(new ImageIcon(ImageManagerImpl.instance.getExileImage()));
break;
@ -96,6 +107,7 @@ public class CardInfoWindowDialog extends MageDialog {
this.setFrameIcon(new ImageIcon(ImageManagerImpl.instance.getTokenIconImage()));
this.setClosable(false);
break;
case OTHER:
default:
// no icon yet
}
@ -149,12 +161,22 @@ public class CardInfoWindowDialog extends MageDialog {
public void loadCards(CardsView showCards, BigCard bigCard, UUID gameId, boolean revertOrder) {
cards.loadCards(showCards, bigCard, gameId, revertOrder);
// additional info for grave windows
if (showType == ShowType.GRAVEYARD) {
int qty = qtyCardTypes(showCards);
String titel = name + "'s Graveyard (" + showCards.size() + ") - " + qty + ((qty == 1) ? " Card Type" : " Card Types");
setTitle(titel);
this.setTitelBarToolTip(titel);
String newTitle = name + "'s graveyard (" + showCards.size() + ") - " + qty + ((qty == 1) ? " card type" : " card types");
setTitle(newTitle);
this.setTitelBarToolTip(newTitle);
}
// additional info for sideboard window
if (showType == ShowType.SIDEBOARD) {
String newTitle = name + "'s sideboard";
setTitle(newTitle);
this.setTitelBarToolTip(newTitle);
}
showAndPositionWindow();
}

View file

@ -77,9 +77,11 @@ public final class GamePanel extends javax.swing.JPanel {
private final Map<UUID, CardInfoWindowDialog> exiles = new HashMap<>();
private final Map<String, CardInfoWindowDialog> revealed = new HashMap<>();
private final Map<String, CardInfoWindowDialog> lookedAt = new HashMap<>();
private final Map<String, CardsView> graveyards = new HashMap<>(); // need to sync selection
private final Map<String, CardInfoWindowDialog> graveyardWindows = new HashMap<>();
private final Map<String, CardInfoWindowDialog> companion = new HashMap<>();
private final Map<String, CardsView> graveyards = new HashMap<>();
private final Map<String, CardsView> sideboards = new HashMap<>(); // need to sync selection
private final Map<String, CardInfoWindowDialog> sideboardWindows = new HashMap<>();
private final ArrayList<ShowCardsDialog> pickTarget = new ArrayList<>();
private final ArrayList<PickPileDialog> pickPile = new ArrayList<>();
private UUID gameId;
@ -246,25 +248,29 @@ public final class GamePanel extends javax.swing.JPanel {
if (pickMultiNumber != null) {
pickMultiNumber.removeDialog();
}
for (CardInfoWindowDialog exileDialog : exiles.values()) {
exileDialog.cleanUp();
exileDialog.removeDialog();
for (CardInfoWindowDialog windowDialog : exiles.values()) {
windowDialog.cleanUp();
windowDialog.removeDialog();
}
for (CardInfoWindowDialog graveyardDialog : graveyardWindows.values()) {
graveyardDialog.cleanUp();
graveyardDialog.removeDialog();
for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
windowDialog.cleanUp();
windowDialog.removeDialog();
}
for (CardInfoWindowDialog revealDialog : revealed.values()) {
revealDialog.cleanUp();
revealDialog.removeDialog();
for (CardInfoWindowDialog windowDialog : sideboardWindows.values()) {
windowDialog.cleanUp();
windowDialog.removeDialog();
}
for (CardInfoWindowDialog lookedAtDialog : lookedAt.values()) {
lookedAtDialog.cleanUp();
lookedAtDialog.removeDialog();
for (CardInfoWindowDialog windowDialog : revealed.values()) {
windowDialog.cleanUp();
windowDialog.removeDialog();
}
for (CardInfoWindowDialog companionDialog : companion.values()) {
companionDialog.cleanUp();
companionDialog.removeDialog();
for (CardInfoWindowDialog windowDialog : lookedAt.values()) {
windowDialog.cleanUp();
windowDialog.removeDialog();
}
for (CardInfoWindowDialog windowDialog : companion.values()) {
windowDialog.cleanUp();
windowDialog.removeDialog();
}
clearPickTargetDialogs();
@ -308,26 +314,29 @@ public final class GamePanel extends javax.swing.JPanel {
playAreaPanel.changeGUISize();
}
for (CardInfoWindowDialog cardInfoWindowDialog : exiles.values()) {
cardInfoWindowDialog.changeGUISize();
for (CardInfoWindowDialog windowDialog : exiles.values()) {
windowDialog.changeGUISize();
}
for (CardInfoWindowDialog cardInfoWindowDialog : revealed.values()) {
cardInfoWindowDialog.changeGUISize();
for (CardInfoWindowDialog windowDialog : revealed.values()) {
windowDialog.changeGUISize();
}
for (CardInfoWindowDialog cardInfoWindowDialog : lookedAt.values()) {
cardInfoWindowDialog.changeGUISize();
for (CardInfoWindowDialog windowDialog : lookedAt.values()) {
windowDialog.changeGUISize();
}
for (CardInfoWindowDialog cardInfoWindowDialog : companion.values()) {
cardInfoWindowDialog.changeGUISize();
for (CardInfoWindowDialog windowDialog : companion.values()) {
windowDialog.changeGUISize();
}
for (CardInfoWindowDialog cardInfoWindowDialog : graveyardWindows.values()) {
cardInfoWindowDialog.changeGUISize();
for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
windowDialog.changeGUISize();
}
for (ShowCardsDialog showCardsDialog : pickTarget) {
showCardsDialog.changeGUISize();
for (CardInfoWindowDialog windowDialog : sideboardWindows.values()) {
windowDialog.changeGUISize();
}
for (PickPileDialog pickPileDialog : pickPile) {
pickPileDialog.changeGUISize();
for (ShowCardsDialog windowDialog : pickTarget) {
windowDialog.changeGUISize();
}
for (PickPileDialog windowDialog : pickPile) {
windowDialog.changeGUISize();
}
this.revalidate();
@ -575,7 +584,7 @@ public final class GamePanel extends javax.swing.JPanel {
}
PlayerView player = game.getPlayers().get(playerSeat);
PlayAreaPanel playAreaPanel = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this,
new PlayAreaPanelOptions(game.isPlayer(), game.isPlayer(), game.isRollbackTurnsAllowed(), row == 0));
new PlayAreaPanelOptions(game.isPlayer(), player.isHuman(), game.isPlayer(), game.isRollbackTurnsAllowed(), row == 0));
players.put(player.getPlayerId(), playAreaPanel);
playersWhoLeft.put(player.getPlayerId(), false);
GridBagConstraints c = new GridBagConstraints();
@ -619,7 +628,7 @@ public final class GamePanel extends javax.swing.JPanel {
}
player = game.getPlayers().get(playerNum);
PlayAreaPanel playerPanel = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this,
new PlayAreaPanelOptions(game.isPlayer(), false, game.isRollbackTurnsAllowed(), row == 0));
new PlayAreaPanelOptions(game.isPlayer(), player.isHuman(), false, game.isRollbackTurnsAllowed(), row == 0));
players.put(player.getPlayerId(), playerPanel);
playersWhoLeft.put(player.getPlayerId(), false);
c = new GridBagConstraints();
@ -790,16 +799,29 @@ public final class GamePanel extends javax.swing.JPanel {
if (player.getPlayerId().equals(playerId)) {
skipButtons.updateFromPlayer(player);
}
// update open or remove closed graveyard windows
graveyards.put(player.getName(), player.getGraveyard());
if (graveyardWindows.containsKey(player.getName())) {
CardInfoWindowDialog cardInfoWindowDialog = graveyardWindows.get(player.getName());
if (cardInfoWindowDialog.isClosed()) {
CardInfoWindowDialog windowDialog = graveyardWindows.get(player.getName());
if (windowDialog.isClosed()) {
graveyardWindows.remove(player.getName());
} else {
cardInfoWindowDialog.loadCards(player.getGraveyard(), bigCard, gameId, false);
windowDialog.loadCards(player.getGraveyard(), bigCard, gameId, false);
}
}
// update open or remove closed sideboard windows
sideboards.put(player.getName(), player.getSideboard());
if (sideboardWindows.containsKey(player.getName())) {
CardInfoWindowDialog windowDialog = sideboardWindows.get(player.getName());
if (windowDialog.isClosed()) {
sideboardWindows.remove(player.getName());
} else {
windowDialog.loadCards(player.getSideboard(), bigCard, gameId, false);
}
}
// show top card window
if (player.getTopCard() != null) {
CardsView cardsView = new CardsView();
@ -852,8 +874,12 @@ public final class GamePanel extends javax.swing.JPanel {
exiles.get(exile.getId()).loadCards(exile, bigCard, gameId);
}
// reveal and look at dialogs can unattached, so windows opened by game doesn't have it
showRevealed(lastGameData.game);
showLookedAt(lastGameData.game);
// sideboard dialogs is unattached all the time -- user opens it by command
showCompanion(lastGameData.game);
if (!lastGameData.game.getCombat().isEmpty()) {
CombatManager.instance.showCombat(lastGameData.game.getCombat(), gameId);
@ -1147,40 +1173,46 @@ public final class GamePanel extends javax.swing.JPanel {
// Called if the game frame is deactivated because the tabled the deck editor or other frames go to foreground
public void deactivated() {
// hide the non modal windows (because otherwise they are shown on top of the new active pane)
for (CardInfoWindowDialog exileDialog : exiles.values()) {
exileDialog.hideDialog();
for (CardInfoWindowDialog windowDialog : exiles.values()) {
windowDialog.hideDialog();
}
for (CardInfoWindowDialog graveyardDialog : graveyardWindows.values()) {
graveyardDialog.hideDialog();
for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
windowDialog.hideDialog();
}
for (CardInfoWindowDialog revealDialog : revealed.values()) {
revealDialog.hideDialog();
for (CardInfoWindowDialog windowDialog : revealed.values()) {
windowDialog.hideDialog();
}
for (CardInfoWindowDialog lookedAtDialog : lookedAt.values()) {
lookedAtDialog.hideDialog();
for (CardInfoWindowDialog windowDialog : lookedAt.values()) {
windowDialog.hideDialog();
}
for (CardInfoWindowDialog companionDialog : companion.values()) {
companionDialog.hideDialog();
for (CardInfoWindowDialog windowDialog : companion.values()) {
windowDialog.hideDialog();
}
for (CardInfoWindowDialog windowDialog : sideboardWindows.values()) {
windowDialog.hideDialog();
}
}
// Called if the game frame comes to front again
public void activated() {
// hide the non modal windows (because otherwise they are shown on top of the new active pane)
for (CardInfoWindowDialog exileDialog : exiles.values()) {
exileDialog.show();
for (CardInfoWindowDialog windowDialog : exiles.values()) {
windowDialog.show();
}
for (CardInfoWindowDialog graveyardDialog : graveyardWindows.values()) {
graveyardDialog.show();
for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
windowDialog.show();
}
for (CardInfoWindowDialog revealDialog : revealed.values()) {
revealDialog.show();
for (CardInfoWindowDialog windowDialog : revealed.values()) {
windowDialog.show();
}
for (CardInfoWindowDialog lookedAtDialog : lookedAt.values()) {
lookedAtDialog.show();
for (CardInfoWindowDialog windowDialog : lookedAt.values()) {
windowDialog.show();
}
for (CardInfoWindowDialog companionDialog : companion.values()) {
companionDialog.show();
for (CardInfoWindowDialog windowDialog : companion.values()) {
windowDialog.show();
}
for (CardInfoWindowDialog windowDialog : sideboardWindows.values()) {
windowDialog.show();
}
}
@ -1197,9 +1229,40 @@ public final class GamePanel extends javax.swing.JPanel {
CardInfoWindowDialog newGraveyard = new CardInfoWindowDialog(ShowType.GRAVEYARD, playerName);
graveyardWindows.put(playerName, newGraveyard);
MageFrame.getDesktop().add(newGraveyard, JLayeredPane.PALETTE_LAYER);
// use graveyards to sync selection (don't use player data here)
newGraveyard.loadCards(graveyards.get(playerName), bigCard, gameId, false);
}
public void openSideboardWindow(UUID playerId) {
if (lastGameData == null) {
return;
}
PlayerView playerView = lastGameData.game.getPlayers().stream()
.filter(p -> p.getPlayerId().equals(playerId))
.findFirst()
.orElse(null);
if (playerView == null) {
return;
}
if (sideboardWindows.containsKey(playerView.getName())) {
CardInfoWindowDialog windowDialog = sideboardWindows.get(playerView.getName());
if (windowDialog.isVisible()) {
windowDialog.hideDialog();
} else {
windowDialog.show();
}
return;
}
CardInfoWindowDialog windowDialog = new CardInfoWindowDialog(ShowType.SIDEBOARD, playerView.getName());
sideboardWindows.put(playerView.getName(), windowDialog);
MageFrame.getDesktop().add(windowDialog, JLayeredPane.PALETTE_LAYER);
// use sideboards to sync selection (don't use player data here)
windowDialog.loadCards(sideboards.get(playerView.getName()), bigCard, gameId, false);
}
public void openTopLibraryWindow(String playerName) {
String title = playerName + "'s top library card";
if (revealed.containsKey(title)) {
@ -1387,6 +1450,25 @@ public final class GamePanel extends javax.swing.JPanel {
}
}
// sideboard
if (needZone == Zone.OUTSIDE || needZone == Zone.ALL) {
for (PlayerView player : lastGameData.game.getPlayers()) {
for (Map.Entry<UUID, CardView> card : player.getSideboard().entrySet()) {
if (needSelectable.contains(card.getKey())) {
card.getValue().setChoosable(true);
}
if (needChoosen.contains(card.getKey())) {
card.getValue().setSelected(true);
}
if (needPlayable.containsObject(card.getKey())) {
card.getValue().setPlayableStats(needPlayable.getStats(card.getKey()));
}
}
}
}
// sideboards (old windows all the time, e.g. unattached from game data)
prepareSelectableWindows(sideboardWindows.values(), needSelectable, needChoosen, needPlayable);
// exile
if (needZone == Zone.EXILED || needZone == Zone.ALL) {
// exile from player panel
@ -1403,6 +1485,7 @@ public final class GamePanel extends javax.swing.JPanel {
}
}
}
// exile from windows
for (ExileView exile : lastGameData.game.getExile()) {
for (Map.Entry<UUID, CardView> card : exile.entrySet()) {

View file

@ -1,89 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.3" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="jPanel1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="357" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jScrollPane1" alignment="1" pref="278" max="32767" attributes="0"/>
<Component id="jPanel1" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="jPanel1">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.BevelBorderInfo">
<BevelBorder/>
</Border>
</Property>
</Properties>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="manaPool" alignment="0" pref="116" max="32767" attributes="1"/>
<Component id="playerPanel" alignment="0" max="32767" attributes="1"/>
<Component id="btnCheat" alignment="0" pref="116" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="playerPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="manaPool" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="17" max="32767" attributes="0"/>
<Component id="btnCheat" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="mage.client.game.PlayerPanel" name="playerPanel">
</Component>
<Component class="mage.client.game.ManaPool" name="manaPool">
</Component>
<Component class="javax.swing.JButton" name="btnCheat">
<Properties>
<Property name="text" type="java.lang.String" value="Cheat"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnCheatActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="mage.client.game.BattlefieldPanel" name="battlefieldPanel">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JLayeredPaneSupportLayout"/>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View file

@ -24,6 +24,8 @@ import java.util.UUID;
import static mage.client.dialog.PreferencesDialog.*;
/**
* GUI: play area panel (player with avatar/mana panel + battlefield panel)
*
* @author BetaSteward_at_googlemail.com
*/
public class PlayAreaPanel extends javax.swing.JPanel {
@ -441,14 +443,28 @@ public class PlayAreaPanel extends javax.swing.JPanel {
popupMenu.addSeparator();
menuItem = new JMenuItem("<html>View current deck");
menuItem.setMnemonic(KeyEvent.VK_V);
// view deck
menuItem = new JMenuItem("<html>View player's deck");
menuItem.setMnemonic(KeyEvent.VK_D);
popupMenu.add(menuItem);
// View limited deck
menuItem.addActionListener(e -> {
SessionHandler.sendPlayerAction(PlayerAction.VIEW_LIMITED_DECK, gameId, null);
});
// view sideboard (allows to view only own sideboard or computer)
// it's a client side checks... same checks must be on server side too (see PlayerView)
if (options.playerItself || !options.isHuman) {
String menuCaption = "<html>View my sideboard";
if (!options.isHuman) {
menuCaption = "<html>View computer's sideboard";
}
menuItem = new JMenuItem(menuCaption);
menuItem.setMnemonic(KeyEvent.VK_S);
popupMenu.add(menuItem);
menuItem.addActionListener(e -> {
SessionHandler.sendPlayerAction(PlayerAction.VIEW_SIDEBOARD, gameId, playerId);
});
}
}
private void addPopupMenuWatcher() {

View file

@ -8,8 +8,9 @@ package mage.client.game;
*/
public class PlayAreaPanelOptions {
public PlayAreaPanelOptions(boolean isPlayer, boolean playerItself, boolean rollbackTurnsAllowed, boolean topRow) {
public PlayAreaPanelOptions(boolean isPlayer, boolean isHuman, boolean playerItself, boolean rollbackTurnsAllowed, boolean topRow) {
this.isPlayer = isPlayer;
this.isHuman = isHuman;
this.playerItself = playerItself;
this.rollbackTurnsAllowed = rollbackTurnsAllowed;
this.topRow = topRow;
@ -18,22 +19,27 @@ public class PlayAreaPanelOptions {
/**
* true if the client is a player / false if the client is a watcher
*/
public boolean isPlayer = false;
public boolean isPlayer;
/**
* true if the player is the human, not computer
*/
public boolean isHuman;
/**
* true if the player is the client player itself, false if the player is
* another player playing with the clinet player
* another player playing with the client player
*/
public boolean playerItself = false;
public boolean playerItself;
/**
* true if the player can rollback turns if all players agree
*/
public boolean rollbackTurnsAllowed = false;
public boolean rollbackTurnsAllowed;
/**
* true if the battlefield is on the top row of player areas
*/
public boolean topRow = false;
public boolean topRow;
}

View file

@ -391,6 +391,12 @@ public class CallbackClientImpl implements CallbackClient {
break;
}
case VIEW_SIDEBOARD: {
TableClientMessage message = (TableClientMessage) callback.getData();
viewSideboard(message.getGameId(), message.getPlayerId());
break;
}
case CONSTRUCT: {
TableClientMessage message = (TableClientMessage) callback.getData();
DeckView deckView = message.getDeck();
@ -616,6 +622,15 @@ public class CallbackClientImpl implements CallbackClient {
frame.showDeckEditor(DeckEditorMode.VIEW_LIMITED_DECK, deck, tableId, time);
}
protected void viewSideboard(UUID gameId, UUID playerId) {
SwingUtilities.invokeLater(() -> {
GamePanel panel = MageFrame.getGame(gameId);
if (panel != null) {
panel.openSideboardWindow(playerId);
}
});
}
private void handleException(Exception ex) {
logger.fatal("Client error\n", ex);
String errorMessage = ex.getMessage();