From 28473c7bd0978199063af652b7c8661b3efa01c3 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Wed, 21 Jul 2021 13:44:35 +0400 Subject: [PATCH] * GUI: added popup menu to view player's outside/sideboard at any time (allows to view only own or computer's sideboards); --- .../client/dialog/CardInfoWindowDialog.java | 30 ++- .../main/java/mage/client/game/GamePanel.java | 193 +++++++++++++----- .../java/mage/client/game/PlayAreaPanel.form | 89 -------- .../java/mage/client/game/PlayAreaPanel.java | 24 ++- .../client/game/PlayAreaPanelOptions.java | 18 +- .../client/remote/CallbackClientImpl.java | 15 ++ .../callback/ClientCallbackMethod.java | 1 + .../src/main/java/mage/view/PlayerView.java | 18 ++ .../src/main/java/mage/server/User.java | 4 + .../java/mage/server/game/GameController.java | 26 ++- .../abilities/effects/ContinuousEffects.java | 11 +- .../java/mage/constants/PlayerAction.java | 1 + 12 files changed, 265 insertions(+), 165 deletions(-) delete mode 100644 Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.form diff --git a/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java b/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java index 5b4c262ab9..f4d178ea4f 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/CardInfoWindowDialog.java @@ -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(); } diff --git a/Mage.Client/src/main/java/mage/client/game/GamePanel.java b/Mage.Client/src/main/java/mage/client/game/GamePanel.java index 794a20804a..d1bd6e8eef 100644 --- a/Mage.Client/src/main/java/mage/client/game/GamePanel.java +++ b/Mage.Client/src/main/java/mage/client/game/GamePanel.java @@ -77,9 +77,11 @@ public final class GamePanel extends javax.swing.JPanel { private final Map exiles = new HashMap<>(); private final Map revealed = new HashMap<>(); private final Map lookedAt = new HashMap<>(); + private final Map graveyards = new HashMap<>(); // need to sync selection private final Map graveyardWindows = new HashMap<>(); private final Map companion = new HashMap<>(); - private final Map graveyards = new HashMap<>(); + private final Map sideboards = new HashMap<>(); // need to sync selection + private final Map sideboardWindows = new HashMap<>(); private final ArrayList pickTarget = new ArrayList<>(); private final ArrayList 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 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 card : exile.entrySet()) { diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.form b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.form deleted file mode 100644 index 6c0820794f..0000000000 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.form +++ /dev/null @@ -1,89 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java index 6d6ead40a6..fbde9c3a33 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanel.java @@ -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("View current deck"); - menuItem.setMnemonic(KeyEvent.VK_V); + // view deck + menuItem = new JMenuItem("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 = "View my sideboard"; + if (!options.isHuman) { + menuCaption = "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() { diff --git a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java index 32c3927727..164d6d4a41 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayAreaPanelOptions.java @@ -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; } diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index 8b7e42099d..e256f7307c 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -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(); diff --git a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java index 5fff7629eb..53ec476e7f 100644 --- a/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java +++ b/Mage.Common/src/main/java/mage/interfaces/callback/ClientCallbackMethod.java @@ -14,6 +14,7 @@ public enum ClientCallbackMethod { START_TOURNAMENT("startTournament"), SIDEBOARD("sideboard"), VIEW_LIMITED_DECK("viewLimitedDeck"), + VIEW_SIDEBOARD("viewSideboard"), CONSTRUCT("construct"), SHOW_USERMESSAGE("showUserMessage"), WATCHGAME("watchGame"), diff --git a/Mage.Common/src/main/java/mage/view/PlayerView.java b/Mage.Common/src/main/java/mage/view/PlayerView.java index 0d0f433605..30784ba083 100644 --- a/Mage.Common/src/main/java/mage/view/PlayerView.java +++ b/Mage.Common/src/main/java/mage/view/PlayerView.java @@ -24,6 +24,7 @@ public class PlayerView implements Serializable { private final UUID playerId; private final String name; private final boolean controlled; // gui: player is current user + private final boolean isHuman; // human or computer private final int life; private final Counters counters; private final int wins; @@ -38,6 +39,7 @@ public class PlayerView implements Serializable { private final ManaPoolView manaPool; private final CardsView graveyard = new CardsView(); private final CardsView exile = new CardsView(); + private final CardsView sideboard = new CardsView(); private final Map battlefield = new LinkedHashMap<>(); private final CardView topCard; private final UserData userData; @@ -58,6 +60,7 @@ public class PlayerView implements Serializable { this.playerId = player.getId(); this.name = player.getName(); this.controlled = player.getId().equals(createdForPlayerId); + this.isHuman = player.isHuman(); this.life = player.getLife(); this.counters = player.getCounters(); this.wins = player.getMatchPlayer().getWins(); @@ -85,6 +88,13 @@ public class PlayerView implements Serializable { } } } + if (this.controlled || !player.isHuman()) { + // sideboard available for itself or for computer only + for (Card card : player.getSideboard().getCards(game)) { + sideboard.put(card.getId(), new CardView(card, game, false)); + } + } + try { for (Permanent permanent : state.getBattlefield().getAllPermanents()) { if (showInBattlefield(permanent, state)) { @@ -167,6 +177,10 @@ public class PlayerView implements Serializable { return this.controlled; } + public boolean isHuman() { + return this.isHuman; + } + public int getLife() { return this.life; } @@ -207,6 +221,10 @@ public class PlayerView implements Serializable { return exile; } + public CardsView getSideboard() { + return this.sideboard; + } + public Map getBattlefield() { return this.battlefield; } diff --git a/Mage.Server/src/main/java/mage/server/User.java b/Mage.Server/src/main/java/mage/server/User.java index cb0dba3047..bf7f025da4 100644 --- a/Mage.Server/src/main/java/mage/server/User.java +++ b/Mage.Server/src/main/java/mage/server/User.java @@ -259,6 +259,10 @@ public class User { fireCallback(new ClientCallback(ClientCallbackMethod.VIEW_LIMITED_DECK, tableId, new TableClientMessage(deck, tableId, time, limited))); } + public void ccViewSideboard(final UUID tableId, final UUID gameId, final UUID targetPlayerId) { + fireCallback(new ClientCallback(ClientCallbackMethod.VIEW_SIDEBOARD, tableId, new TableClientMessage(gameId, targetPlayerId))); + } + public void ccConstruct(final Deck deck, final UUID tableId, final int time) { fireCallback(new ClientCallback(ClientCallbackMethod.CONSTRUCT, tableId, new TableClientMessage(deck, tableId, time))); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index 8317ea91cd..59b7ca3251 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -598,6 +598,12 @@ public class GameController implements GameCallback { case VIEW_LIMITED_DECK: viewLimitedDeck(getPlayerId(userId), userId); break; + case VIEW_SIDEBOARD: + if (data instanceof UUID) { + UUID targetPlayerId = (UUID) data; + viewSideboard(getPlayerId(userId), userId, targetPlayerId); + } + break; default: game.sendPlayerAction(playerAction, getPlayerId(userId), data); } @@ -656,13 +662,13 @@ public class GameController implements GameCallback { } } - private void viewLimitedDeck(UUID userIdRequester, UUID origId) { - Player viewLimitedDeckPlayer = game.getPlayer(userIdRequester); + private void viewLimitedDeck(UUID playerId, UUID userId) { + Player viewLimitedDeckPlayer = game.getPlayer(playerId); if (viewLimitedDeckPlayer != null) { if (viewLimitedDeckPlayer.isHuman()) { for (MatchPlayer p : managerFactory.tableManager().getTable(tableId).getMatch().getPlayers()) { - if (p.getPlayer().getId().equals(userIdRequester)) { - Optional u = managerFactory.userManager().getUser(origId); + if (p.getPlayer().getId().equals(playerId)) { + Optional u = managerFactory.userManager().getUser(userId); if (u.isPresent() && p.getDeck() != null) { u.get().ccViewLimitedDeck(p.getDeck(), tableId, requestsOpen, true); } @@ -672,6 +678,18 @@ public class GameController implements GameCallback { } } + private void viewSideboard(UUID playerId, UUID userId, UUID targetPlayerId) { + Player needPlayer = game.getPlayer(playerId); + if (needPlayer != null && needPlayer.isHuman()) { + for (MatchPlayer p : managerFactory.tableManager().getTable(tableId).getMatch().getPlayers()) { + if (p.getPlayer().getId().equals(playerId)) { + Optional u = managerFactory.userManager().getUser(userId); + u.ifPresent(user -> user.ccViewSideboard(tableId, game.getId(), targetPlayerId)); + } + } + } + } + public void cheat(UUID userId, UUID playerId, DeckCardLists deckList) { try { Deck deck = Deck.load(deckList, false, false); diff --git a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java index 35659974ee..434286e542 100644 --- a/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/main/java/mage/abilities/effects/ContinuousEffects.java @@ -592,9 +592,14 @@ public class ContinuousEffects implements Serializable { Map keyChoices = new HashMap<>(); for (ApprovingObject approvingObject : possibleApprovingObjects) { MageObject mageObject = game.getObject(approvingObject.getApprovingAbility().getSourceId()); - keyChoices.put(approvingObject.getApprovingAbility().getId().toString(), - (approvingObject.getApprovingAbility().getRule(mageObject == null ? "" : mageObject.getName())) - + (mageObject == null ? "" : " (" + mageObject.getIdName() + ")")); + String choiceKey = approvingObject.getApprovingAbility().getId().toString(); + String choiceValue; + if (mageObject == null) { + choiceValue = approvingObject.getApprovingAbility().getRule(); + } else { + choiceValue = mageObject.getIdName() + ": " + approvingObject.getApprovingAbility().getRule(mageObject.getName()); + } + keyChoices.put(choiceKey, choiceValue); } Choice choicePermitting = new ChoiceImpl(true); choicePermitting.setMessage("Choose the permitting object"); diff --git a/Mage/src/main/java/mage/constants/PlayerAction.java b/Mage/src/main/java/mage/constants/PlayerAction.java index ef43bfdb9f..052d42a67a 100644 --- a/Mage/src/main/java/mage/constants/PlayerAction.java +++ b/Mage/src/main/java/mage/constants/PlayerAction.java @@ -59,5 +59,6 @@ public enum PlayerAction { HOLD_PRIORITY, UNHOLD_PRIORITY, VIEW_LIMITED_DECK, + VIEW_SIDEBOARD, TOGGLE_RECORD_MACRO }