GUI - new card hints window features:

* new help window can be opened from a player panel;
* it collect and show all visible game hints from all players and all zones;
* it updates in real time on game update;
* allows to customize visible data;
* allows to open multiple windows (current limit is 5 windows, can be slow to render);
* allows to minimize opened windows;
* workable card popup on mouse move over card name or card id;
* filter modes:
  * all - show hints from all players;
  * player - show hints from single player;
* group mode:
  * by hints - show same hints as one with all used cards;
  * by cards - show full cards list with own hints;
* search mode:
  * allows to filter card hints by player name, card name, card id or card hint;
  * allows to search multiple words (equals to "or")
* current limitation:
  * card popup shows a card instead a real object, e.g. miss card hints in it (relelated to game logs problem);
  * unsupport of emblems, dungeons and other non card objects from a command zone;
  * unsupport of revealed and library's top cards;

GUI - player's panel improves:
* added hints helper button;
* added player hithlight as possible target in choose dialogs;
* improved player name button in small mode;
* fixed wrong height in small mode;

Other fixes:
* game logs: added card popup support for logs with custom object name;
This commit is contained in:
Oleg Agafonov 2023-11-18 14:48:25 +04:00
parent ca80849249
commit 2bbe2b3c43
16 changed files with 1229 additions and 75 deletions

View file

@ -86,6 +86,8 @@ public final class GamePanel extends javax.swing.JPanel {
private final Map<String, CardInfoWindowDialog> sideboardWindows = new HashMap<>();
private final ArrayList<ShowCardsDialog> pickTarget = new ArrayList<>();
private final ArrayList<PickPileDialog> pickPile = new ArrayList<>();
private final Map<String, CardHintsHelperDialog> cardHintsWindows = new LinkedHashMap<>();
private UUID gameId;
private UUID playerId; // playerId of the player
GamePane gamePane;
@ -275,6 +277,10 @@ public final class GamePanel extends javax.swing.JPanel {
windowDialog.cleanUp();
windowDialog.removeDialog();
}
for (CardHintsHelperDialog windowDialog : cardHintsWindows.values()) {
windowDialog.cleanUp();
windowDialog.removeDialog();
}
clearPickDialogs();
@ -350,6 +356,9 @@ public final class GamePanel extends javax.swing.JPanel {
for (CardInfoWindowDialog windowDialog : sideboardWindows.values()) {
windowDialog.changeGUISize();
}
for (CardHintsHelperDialog windowDialog : cardHintsWindows.values()) {
windowDialog.changeGUISize();
}
for (ShowCardsDialog windowDialog : pickTarget) {
windowDialog.changeGUISize();
}
@ -886,16 +895,25 @@ public final class GamePanel extends javax.swing.JPanel {
GameManager.instance.setStackSize(lastGameData.game.getStack().size());
displayStack(lastGameData.game, bigCard, feedbackPanel, gameId);
// auto-show exile views
for (ExileView exile : lastGameData.game.getExile()) {
if (!exiles.containsKey(exile.getId())) {
CardInfoWindowDialog newExile = new CardInfoWindowDialog(ShowType.EXILE, exile.getName());
exiles.put(exile.getId(), newExile);
MageFrame.getDesktop().add(newExile, JLayeredPane.PALETTE_LAYER);
newExile.show();
CardInfoWindowDialog exileWindow = exiles.getOrDefault(exile.getId(), null);
if (exileWindow == null) {
exileWindow = new CardInfoWindowDialog(ShowType.EXILE, exile.getName());
exiles.put(exile.getId(), exileWindow);
MageFrame.getDesktop().add(exileWindow, JLayeredPane.PALETTE_LAYER);
exileWindow.show();
}
exiles.get(exile.getId()).loadCards(exile, bigCard, gameId);
exileWindow.loadCards(exile, bigCard, gameId);
}
// update open or remove closed card hints windows
clearClosedCardHintsWindows();
cardHintsWindows.forEach((s, windowDialog) -> {
// TODO: optimize for multiple windows (prepare data here and send it for filters/groups)
windowDialog.loadHints(lastGameData.game);
});
// reveal and look at dialogs can unattached, so windows opened by game doesn't have it
showRevealed(lastGameData.game);
showLookedAt(lastGameData.game);
@ -1197,24 +1215,28 @@ 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)
// TODO: is it need to hide other dialogs like graveyards (CardsView)?
for (CardInfoWindowDialog windowDialog : exiles.values()) {
windowDialog.hideDialog();
}
for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
windowDialog.hideDialog();
}
for (CardInfoWindowDialog windowDialog : revealed.values()) {
windowDialog.hideDialog();
}
for (CardInfoWindowDialog windowDialog : lookedAt.values()) {
windowDialog.hideDialog();
}
for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
windowDialog.hideDialog();
}
for (CardInfoWindowDialog windowDialog : companion.values()) {
windowDialog.hideDialog();
}
for (CardInfoWindowDialog windowDialog : sideboardWindows.values()) {
windowDialog.hideDialog();
}
for (CardHintsHelperDialog windowDialog : cardHintsWindows.values()) {
windowDialog.hideDialog();
}
}
// Called if the game frame comes to front again
@ -1223,21 +1245,24 @@ public final class GamePanel extends javax.swing.JPanel {
for (CardInfoWindowDialog windowDialog : exiles.values()) {
windowDialog.show();
}
for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
windowDialog.show();
}
for (CardInfoWindowDialog windowDialog : revealed.values()) {
windowDialog.show();
}
for (CardInfoWindowDialog windowDialog : lookedAt.values()) {
windowDialog.show();
}
for (CardInfoWindowDialog windowDialog : graveyardWindows.values()) {
windowDialog.show();
}
for (CardInfoWindowDialog windowDialog : companion.values()) {
windowDialog.show();
}
for (CardInfoWindowDialog windowDialog : sideboardWindows.values()) {
windowDialog.show();
}
for (CardHintsHelperDialog windowDialog : cardHintsWindows.values()) {
windowDialog.show();
}
}
public void openGraveyardWindow(String playerName) {
@ -1257,6 +1282,29 @@ public final class GamePanel extends javax.swing.JPanel {
newGraveyard.loadCards(graveyards.get(playerName), bigCard, gameId, false);
}
private void clearClosedCardHintsWindows() {
cardHintsWindows.entrySet().removeIf(entry -> entry.getValue().isClosed());
}
public void openCardHintsWindow(String code) {
// clear closed
clearClosedCardHintsWindows();
// too many dialogs can cause bad GUI performance, so limit it
if (cardHintsWindows.size() > CardHintsHelperDialog.GUI_MAX_CARD_HINTS_DIALOGS_PER_GAME) {
// show last one instead
cardHintsWindows.values().stream().reduce((a, b) -> b).ifPresent(CardHintsHelperDialog::show);
return;
}
// open new
CardHintsHelperDialog newDialog = new CardHintsHelperDialog();
newDialog.setGameData(this.lastGameData.game, this.gameId, this.bigCard);
cardHintsWindows.put(code + UUID.randomUUID(), newDialog);
MageFrame.getDesktop().add(newDialog, JLayeredPane.PALETTE_LAYER);
newDialog.loadHints(lastGameData.game);
}
public void openSideboardWindow(UUID playerId) {
if (lastGameData == null) {
return;

View file

@ -33,6 +33,7 @@ public class PlayAreaPanel extends javax.swing.JPanel {
private final JPopupMenu popupMenu;
private UUID playerId;
private UUID gameId;
private boolean isMe = false;
private boolean smallMode = false;
private boolean playingMode = true;
private final GamePanel gamePanel;
@ -46,6 +47,7 @@ public class PlayAreaPanel extends javax.swing.JPanel {
public static final int PANEL_HEIGHT = 263;
public static final int PANEL_HEIGHT_SMALL = 210;
private static final int PANEL_HEIGHT_EXTRA_FOR_ME = 25;
/**
* Creates new form PlayAreaPanel
@ -515,6 +517,7 @@ public class PlayAreaPanel extends javax.swing.JPanel {
this.battlefieldPanel.init(gameId, bigCard);
this.gameId = gameId;
this.playerId = player.getPlayerId();
this.isMe = player.getControlled();
this.btnCheat.setVisible(SessionHandler.isTestMode());
}
@ -562,14 +565,15 @@ public class PlayAreaPanel extends javax.swing.JPanel {
public void sizePlayer(boolean smallMode) {
this.playerPanel.sizePlayerPanel(smallMode);
this.smallMode = smallMode;
int extraForMe = this.isMe ? PANEL_HEIGHT_EXTRA_FOR_ME : 0;
if (smallMode) {
this.playerPanel.setPreferredSize(new Dimension(92, PANEL_HEIGHT_SMALL));
this.playerPanel.setPreferredSize(new Dimension(92, PANEL_HEIGHT_SMALL + extraForMe));
//this.jScrollPane1.setPreferredSize(new Dimension(160, 160));
this.battlefieldPanel.setPreferredSize(new Dimension(160, PANEL_HEIGHT_SMALL));
this.battlefieldPanel.setPreferredSize(new Dimension(160, PANEL_HEIGHT_SMALL + extraForMe));
} else {
this.playerPanel.setPreferredSize(new Dimension(92, PANEL_HEIGHT));
this.playerPanel.setPreferredSize(new Dimension(92, PANEL_HEIGHT + extraForMe));
//this.jScrollPane1.setPreferredSize(new Dimension(160, 212));
this.battlefieldPanel.setPreferredSize(new Dimension(160, PANEL_HEIGHT));
this.battlefieldPanel.setPreferredSize(new Dimension(160, PANEL_HEIGHT + extraForMe));
}
}

View file

@ -46,6 +46,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
private UUID playerId;
private UUID gameId;
private PlayerView player;
private boolean isMe;
private BigCard bigCard;
@ -53,11 +54,13 @@ public class PlayerPanelExt extends javax.swing.JPanel {
private static final int PANEL_WIDTH = 94;
private static final int PANEL_HEIGHT = 262;
private static final int PANEL_HEIGHT_SMALL = 242;
private static final int PANEL_HEIGHT_SMALL = 210;
private static final int PANEL_HEIGHT_EXTRA_FOR_ME = 25;
private static final int MANA_LABEL_SIZE_HORIZONTAL = 20;
private static final Border GREEN_BORDER = new LineBorder(Color.green, 3);
private static final Border RED_BORDER = new LineBorder(Color.red, 2);
private static final Border YELLOW_BORDER = new LineBorder(Color.yellow, 3);
private static final Border EMPTY_BORDER = BorderFactory.createEmptyBorder(0, 0, 0, 0);
private final Color inactiveBackgroundColor;
private final Color activeBackgroundColor;
@ -92,8 +95,10 @@ public class PlayerPanelExt extends javax.swing.JPanel {
this.gameId = gameId;
this.playerId = playerId;
this.bigCard = bigCard;
cheat.setVisible(SessionHandler.isTestMode() && controlled);
this.isMe = controlled;
cheat.setVisible(SessionHandler.isTestMode() && this.isMe);
cheat.setFocusable(false);
toolHintsHelper.setVisible(this.isMe);
flagName = null;
if (priorityTime > 0) {
long delay = 1000L;
@ -355,6 +360,12 @@ public class PlayerPanelExt extends javax.swing.JPanel {
}
}
// possible targeting
if (possibleTargets != null && possibleTargets.contains(this.playerId)) {
this.avatar.setBorder(YELLOW_BORDER);
this.btnPlayer.setBorder(YELLOW_BORDER);
}
update(player.getManaPool());
}
@ -568,6 +579,13 @@ public class PlayerPanelExt extends javax.swing.JPanel {
cheat.setToolTipText("Cheat button");
cheat.addActionListener(e -> btnCheatActionPerformed(e));
// Tools button
r = new Rectangle(75, 21);
toolHintsHelper = new JButton();
toolHintsHelper.setText("hints");
toolHintsHelper.setToolTipText("Open new card hints helper window");
toolHintsHelper.addActionListener(e -> btnToolHintsHelperActionPerformed(e));
zonesPanel = new JPanel();
zonesPanel.setPreferredSize(new Dimension(100, 60));
zonesPanel.setSize(100, 60);
@ -591,6 +609,9 @@ public class PlayerPanelExt extends javax.swing.JPanel {
cheat.setBounds(40, 2, 25, 21);
zonesPanel.add(cheat);
toolHintsHelper.setBounds(3, 2 + 21 + 2, 75, 21);
zonesPanel.add(toolHintsHelper);
energyExperiencePanel = new JPanel();
energyExperiencePanel.setPreferredSize(new Dimension(100, 20));
energyExperiencePanel.setSize(100, 20);
@ -619,6 +640,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
btnPlayer.setText("Player");
btnPlayer.setVisible(false);
btnPlayer.setToolTipText("Player");
btnPlayer.setPreferredSize(new Dimension(20, 40));
btnPlayer.addActionListener(e -> SessionHandler.sendPlayerUUID(gameId, playerId));
// Add mana symbols
@ -808,7 +830,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
.addGap(6)
.addComponent(avatar, GroupLayout.PREFERRED_SIZE, 80, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(ComponentPlacement.RELATED)
.addComponent(btnPlayer)
.addComponent(btnPlayer, GroupLayout.PREFERRED_SIZE, 30, GroupLayout.PREFERRED_SIZE)
.addComponent(timerLabel)
.addGap(2)
// Life & Hand
@ -912,18 +934,19 @@ public class PlayerPanelExt extends javax.swing.JPanel {
}// </editor-fold>//GEN-END:initComponents
protected void sizePlayerPanel(boolean smallMode) {
int extraForMe = this.isMe ? PANEL_HEIGHT_EXTRA_FOR_ME : 0;
if (smallMode) {
avatar.setVisible(false);
btnPlayer.setVisible(true);
timerLabel.setVisible(true);
panelBackground.setPreferredSize(new Dimension(PANEL_WIDTH - 2, PANEL_HEIGHT_SMALL));
panelBackground.setBounds(0, 0, PANEL_WIDTH - 2, PANEL_HEIGHT_SMALL);
panelBackground.setPreferredSize(new Dimension(PANEL_WIDTH - 2, PANEL_HEIGHT_SMALL + extraForMe));
panelBackground.setBounds(0, 0, PANEL_WIDTH - 2, PANEL_HEIGHT_SMALL + extraForMe);
} else {
avatar.setVisible(true);
btnPlayer.setVisible(false);
timerLabel.setVisible(false);
panelBackground.setPreferredSize(new Dimension(PANEL_WIDTH - 2, PANEL_HEIGHT));
panelBackground.setBounds(0, 0, PANEL_WIDTH - 2, PANEL_HEIGHT);
panelBackground.setPreferredSize(new Dimension(PANEL_WIDTH - 2, PANEL_HEIGHT + extraForMe));
panelBackground.setBounds(0, 0, PANEL_WIDTH - 2, PANEL_HEIGHT + extraForMe);
}
}
@ -952,6 +975,10 @@ public class PlayerPanelExt extends javax.swing.JPanel {
SessionHandler.cheat(gameId, playerId, deckImporter.importDeck("cheat.dck", false));
}
private void btnToolHintsHelperActionPerformed(java.awt.event.ActionEvent evt) {
MageFrame.getGame(gameId).openCardHintsWindow("main");
}
public PlayerView getPlayer() {
return player;
}
@ -984,6 +1011,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
private HoverButton grave;
private HoverButton library;
private JButton cheat;
private JButton toolHintsHelper;
private MageRoundPane panelBackground;
private JLabel timerLabel;