From acfc3d422d77bffd8c1199be3c28dd2c5621e4e8 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 9 May 2024 10:00:34 +0200 Subject: [PATCH 01/11] Add multiple BoardViews handling to AbstractClientGUI --- .../client/ui/swing/AbstractClientGUI.java | 21 ++++- .../client/ui/swing/BoardViewsContainer.java | 94 +++++++++++++++++++ .../megamek/client/ui/swing/ClientGUI.java | 1 + .../megamek/client/ui/swing/IClientGUI.java | 3 + .../megamek/client/ui/swing/SBFClientGUI.java | 2 +- megamek/src/megamek/common/Board.java | 9 ++ 6 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 megamek/src/megamek/client/ui/swing/BoardViewsContainer.java diff --git a/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java b/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java index 6fd6a18b005..ec548dad5ff 100644 --- a/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java @@ -18,7 +18,9 @@ */ package megamek.client.ui.swing; +import megamek.client.IClient; import megamek.client.ui.Messages; +import megamek.client.ui.swing.boardview.BoardView; import megamek.client.ui.swing.util.UIUtil; import megamek.common.Configuration; import megamek.common.preference.ClientPreferences; @@ -30,7 +32,9 @@ import java.awt.event.WindowEvent; import java.io.File; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public abstract class AbstractClientGUI implements IClientGUI { @@ -43,11 +47,22 @@ public abstract class AbstractClientGUI implements IClientGUI { private static final String FILENAME_ICON_256X256 = "megamek-icon-256x256.png"; protected final JFrame frame = new JFrame(Messages.getString("ClientGUI.title")); + private final IClient iClient; - public AbstractClientGUI() { + // BoardViews + protected final Map boardViews = new HashMap<>(); + protected final BoardViewsContainer boardViewsContainer = new BoardViewsContainer(this); + protected final JPanel bvc = new JPanel(); + + public AbstractClientGUI(IClient iClient) { + this.iClient = iClient; initializeFrame(); } + public IClient getIClient() { + return iClient; + } + @Override public JFrame getFrame() { return frame; @@ -120,4 +135,8 @@ void saveSettings() { } protected abstract boolean saveGame(); + + public List boardViews() { + return new ArrayList<>(boardViews.values()); + } } diff --git a/megamek/src/megamek/client/ui/swing/BoardViewsContainer.java b/megamek/src/megamek/client/ui/swing/BoardViewsContainer.java new file mode 100644 index 00000000000..ef699318efe --- /dev/null +++ b/megamek/src/megamek/client/ui/swing/BoardViewsContainer.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MegaMek. + * + * MegaMek is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MegaMek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MegaMek. If not, see . + */ +package megamek.client.ui.swing; + +import megamek.client.ui.swing.boardview.BoardView; +import megamek.client.ui.swing.util.UIUtil; +import megamek.common.Board; + +import javax.swing.*; +import java.awt.*; + +public class BoardViewsContainer { + + /** The panel that displays the BoardView(s) */ + private final JPanel boardViewsContainer = new JPanel(new GridLayout(1, 1)); + + /** The tabbed pane is used when there are multiple boards to display */ + private final JTabbedPane mapTabPane = new JTabbedPane(); + + private final AbstractClientGUI clientGUI; + + public BoardViewsContainer(AbstractClientGUI clientGUI) { + this.clientGUI = clientGUI; + } + + public JPanel getPanel() { + return boardViewsContainer; + } + + /** + * Updates the map display. When there's a single map type in the game, the map is displayed without + * anything special (not tabs). When there's more than one map type, all maps are added to the + * JTabbedPane mapTabPane and this pane is displayed. + */ + private void updateMapTabs() { + boardViewsContainer.removeAll(); + if (clientGUI.boardViews.size() > 1) { + arrangeMultipleBoardViews(); + } else if (clientGUI.boardViews.size() == 1) { + arrangeSingleBoardView(); + } + boardViewsContainer.validate(); + } + + private void arrangeMultipleBoardViews() { + mapTabPane.removeAll(); + for (int boardId : clientGUI.boardViews.keySet()) { + mapTabPane.add(board(boardId).getMapName(), boardView(boardId).getComponent(true)); + mapTabPane.setToolTipTextAt(mapTabPane.getTabCount() - 1, getBoardViewTabTooltip(boardId)); + } + boardViewsContainer.add(mapTabPane); + } + + private void arrangeSingleBoardView() { + // The single BoardView does not use the tabbed pane + int boardId = clientGUI.boardViews.keySet().iterator().next(); + boardViewsContainer.add(board(boardId).getMapName(), boardView(boardId).getComponent(true)); + } + + private String getBoardViewTabTooltip(int boardId) { + //TODO Add embedded and enclosing boards +// Game game = getIClient().getGame(); +// if (game.hasEnclosingBoard(boardId)) { +// Board enclosingBoard = game.getEnclosingBoard(boardView.getBoard()); +// tooltip += "
Located at " + enclosingBoard.embeddedBoardPosition(boardId).getBoardNum() + +// " in " + enclosingBoard; +// } + return "" + UIUtil.guiScaledFontHTML() + board(boardId).getMapName() + ""; + } + + private Board board(int id) { + return clientGUI.getIClient().getIGame().getBoard(id); + } + + private BoardView boardView(int id) { + return clientGUI.boardViews.get(id); + } +} diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java index efbc657762d..d205863f385 100644 --- a/megamek/src/megamek/client/ui/swing/ClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java @@ -107,6 +107,7 @@ public class ClientGUI extends JPanel implements BoardViewListener, IClientGUI, // Note: anything located in menu bars is not located here but in their menu public static final String MAIN_SKIN_NEW = "mainSkinNew"; public static final String MAIN_QUIT = "mainQuit"; + // endregion // region file menu // game submenu public static final String FILE_GAME_NEW = "fileGameNew"; diff --git a/megamek/src/megamek/client/ui/swing/IClientGUI.java b/megamek/src/megamek/client/ui/swing/IClientGUI.java index 914f8d40dee..1acd6afe404 100644 --- a/megamek/src/megamek/client/ui/swing/IClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/IClientGUI.java @@ -18,6 +18,7 @@ */ package megamek.client.ui.swing; +import megamek.client.IClient; import megamek.common.InGameObject; import javax.swing.*; @@ -49,4 +50,6 @@ public interface IClientGUI { void die(); InGameObject getSelectedUnit(); + + IClient getIClient(); } diff --git a/megamek/src/megamek/client/ui/swing/SBFClientGUI.java b/megamek/src/megamek/client/ui/swing/SBFClientGUI.java index 878bedf1326..33e751e1f52 100644 --- a/megamek/src/megamek/client/ui/swing/SBFClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/SBFClientGUI.java @@ -18,7 +18,6 @@ */ package megamek.client.ui.swing; -import megamek.client.Client; import megamek.client.IClient; import megamek.client.SBFClient; import megamek.client.ui.swing.util.MegaMekController; @@ -31,6 +30,7 @@ public class SBFClientGUI extends AbstractClientGUI { private final SBFClient client; public SBFClientGUI(IClient client, MegaMekController c) { + super(client); if (!(client instanceof SBFClient)) { throw new IllegalArgumentException("SBF ClientGUI must use SBF Client!"); } diff --git a/megamek/src/megamek/common/Board.java b/megamek/src/megamek/common/Board.java index 353095c5806..385bdb87e97 100644 --- a/megamek/src/megamek/common/Board.java +++ b/megamek/src/megamek/common/Board.java @@ -113,6 +113,9 @@ public class Board implements Serializable { /** Tags associated with this board to facilitate searching for it. */ private Set tags = new HashSet<>(); + + private final int boardId = 0; + //endregion Variable Declarations //region Constructors @@ -1785,4 +1788,10 @@ public void removeTag(String tag) { public Set getTags() { return Collections.unmodifiableSet(tags); } + + + /** @return The name of this map; this is meant to be displayed in the GUI. */ + public String getMapName() { + return "Board #" + boardId; + } } From 71ec4f019d0c25090d6591cde07922a0506a8094 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 9 May 2024 11:54:00 +0200 Subject: [PATCH 02/11] Use BoardViewsContainer and IBoardView --- .../client/ui/swing/AbstractClientGUI.java | 7 ++- .../client/ui/swing/BoardViewsContainer.java | 43 ++++++++++++++----- .../megamek/client/ui/swing/ClientGUI.java | 31 ++++++------- .../megamek/client/ui/swing/MegaMekGUI.java | 6 +-- .../common/event/GameBoardNewEvent.java | 16 +++++-- 5 files changed, 65 insertions(+), 38 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java b/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java index 8ec3bbc31bb..807e764f063 100644 --- a/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java @@ -20,7 +20,7 @@ import megamek.client.IClient; import megamek.client.ui.Messages; -import megamek.client.ui.swing.boardview.BoardView; +import megamek.client.ui.swing.boardview.*; import megamek.client.ui.swing.util.UIUtil; import megamek.common.Configuration; import megamek.common.preference.ClientPreferences; @@ -56,9 +56,8 @@ public abstract class AbstractClientGUI implements IClientGUI { private final IClient iClient; // BoardViews - protected final Map boardViews = new HashMap<>(); + protected final Map boardViews = new HashMap<>(); protected final BoardViewsContainer boardViewsContainer = new BoardViewsContainer(this); - protected final JPanel bvc = new JPanel(); public AbstractClientGUI(IClient iClient) { this.iClient = iClient; @@ -144,7 +143,7 @@ void saveSettings() { protected abstract boolean saveGame(); - public List boardViews() { + public List boardViews() { return new ArrayList<>(boardViews.values()); } } diff --git a/megamek/src/megamek/client/ui/swing/BoardViewsContainer.java b/megamek/src/megamek/client/ui/swing/BoardViewsContainer.java index ef699318efe..04e1f0672c3 100644 --- a/megamek/src/megamek/client/ui/swing/BoardViewsContainer.java +++ b/megamek/src/megamek/client/ui/swing/BoardViewsContainer.java @@ -18,13 +18,22 @@ */ package megamek.client.ui.swing; -import megamek.client.ui.swing.boardview.BoardView; +import megamek.client.ui.swing.boardview.IBoardView; import megamek.client.ui.swing.util.UIUtil; import megamek.common.Board; import javax.swing.*; import java.awt.*; +import java.util.Objects; +/** + * The BoardViewsContainer manages the JPanel that contains the BoardView(s) of a ClientGUI. When only + * one BoardView is present, it is shown by itself. When multiple BoardViews are present, + * they are arranged as tabs of a TabbedPane. The panel that holds the BoardView(s) is obtained from + * {@link #getPanel()}. + *

The display contents are not automatically updated. Use {@link #updateMapTabs()} after construction + * and later to make it reflect the current set of BoardViews. + */ public class BoardViewsContainer { /** The panel that displays the BoardView(s) */ @@ -35,20 +44,30 @@ public class BoardViewsContainer { private final AbstractClientGUI clientGUI; + /** + * Returns a new BoardViewsContainer. Call {@link #updateMapTabs()} after construction to make it + * reflect the current BoardViews. Requires a non-null AbstractClientGUI as parent. + * + * @param clientGUI The AbstractClientGUI parent + */ public BoardViewsContainer(AbstractClientGUI clientGUI) { - this.clientGUI = clientGUI; + this.clientGUI = Objects.requireNonNull(clientGUI); } - public JPanel getPanel() { + /** + * Returns the JPanel that holds the BoardView(s), either one BoardView by itself or multiple + * BoardViews in a tabbed pane. Add this panel to the view area of the ClientGUI. + * + * @return The panel holding all present BoardViews + */ + public Component getPanel() { return boardViewsContainer; } /** - * Updates the map display. When there's a single map type in the game, the map is displayed without - * anything special (not tabs). When there's more than one map type, all maps are added to the - * JTabbedPane mapTabPane and this pane is displayed. + * Updates the BoardViewsContainer to reflect the current state of ClientGUI's BoardViews. */ - private void updateMapTabs() { + public void updateMapTabs() { boardViewsContainer.removeAll(); if (clientGUI.boardViews.size() > 1) { arrangeMultipleBoardViews(); @@ -61,7 +80,7 @@ private void updateMapTabs() { private void arrangeMultipleBoardViews() { mapTabPane.removeAll(); for (int boardId : clientGUI.boardViews.keySet()) { - mapTabPane.add(board(boardId).getMapName(), boardView(boardId).getComponent(true)); + mapTabPane.add(board(boardId).getMapName(), boardView(boardId).getComponent()); mapTabPane.setToolTipTextAt(mapTabPane.getTabCount() - 1, getBoardViewTabTooltip(boardId)); } boardViewsContainer.add(mapTabPane); @@ -70,7 +89,7 @@ private void arrangeMultipleBoardViews() { private void arrangeSingleBoardView() { // The single BoardView does not use the tabbed pane int boardId = clientGUI.boardViews.keySet().iterator().next(); - boardViewsContainer.add(board(boardId).getMapName(), boardView(boardId).getComponent(true)); + boardViewsContainer.add(board(boardId).getMapName(), boardView(boardId).getComponent()); } private String getBoardViewTabTooltip(int boardId) { @@ -88,7 +107,11 @@ private Board board(int id) { return clientGUI.getIClient().getIGame().getBoard(id); } - private BoardView boardView(int id) { + private IBoardView boardView(int id) { return clientGUI.boardViews.get(id); } + + public void setName(String name) { + boardViewsContainer.setName(name); + } } diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java index d4fa610c3e2..933cfa26dc4 100644 --- a/megamek/src/megamek/client/ui/swing/ClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java @@ -23,7 +23,6 @@ import megamek.MMConstants; import megamek.client.AbstractClient; import megamek.client.Client; -import megamek.client.IClient; import megamek.client.TimerSingleton; import megamek.client.bot.BotClient; import megamek.client.bot.princess.BehaviorSettings; @@ -236,7 +235,6 @@ public class ClientGUI extends AbstractClientGUI implements BoardViewListener, private CollapseWarningSpriteHandler collapseWarningSpriteHandler; private FiringSolutionSpriteHandler firingSolutionSpriteHandler; private final List spriteHandlers = new ArrayList<>(); - private Component bvc; private JPanel panTop; private JSplitPane splitPaneA; private JPanel panA1; @@ -352,11 +350,9 @@ public class ClientGUI extends AbstractClientGUI implements BoardViewListener, * clean up after itself as much as possible, but will not call * System.exit(). */ - public ClientGUI(IClient client, MegaMekController c) { - if (!(client instanceof Client)) { - throw new IllegalArgumentException("TW ClientGUI must use TW Client!"); - } - this.client = (Client) client; + public ClientGUI(Client client, MegaMekController c) { + super(client); + this.client = client; controller = c; panMain.setLayout(cardsMain); panSecondary.setLayout(cardsSecondary); @@ -498,13 +494,14 @@ public void initialize() { client.getGame().addGameListener(gameListener); bv = new BoardView(client.getGame(), controller, this); + boardViews.put(0, bv); bv.addOverlay(new KeyBindingsOverlay(bv)); bv.addOverlay(new PlanetaryConditionsOverlay(bv)); bv.addOverlay(new TurnDetailsOverlay(bv)); bv.getPanel().setPreferredSize(clientGuiPanel.getSize()); bv.setTooltipProvider(new TWBoardViewTooltip(client.getGame(), this, bv)); - bvc = bv.getComponent(); - bvc.setName(CG_BOARDVIEW); + boardViewsContainer.setName(CG_BOARDVIEW); + boardViewsContainer.updateMapTabs(); initializeSpriteHandlers(); panTop = new JPanel(new BorderLayout()); @@ -1656,7 +1653,7 @@ public void setUnitDisplayLocation(boolean visible) { if (GUIP.getDockOnLeft()) { switch (GUIP.getUnitDisplayLocaton()) { case 0: - panA2.add(bvc); + panA2.add(boardViewsContainer.getPanel()); panA2.setVisible(true); getUnitDisplayDialog().add(getUnitDisplay(), BorderLayout.CENTER); getUnitDisplayDialog().setVisible(visible); @@ -1665,7 +1662,7 @@ public void setUnitDisplayLocation(boolean visible) { hideEmptyPanel(panA1, splitPaneA, 0.0); break; case 1: - panA2.add(bvc); + panA2.add(boardViewsContainer.getPanel()); panA2.setVisible(true); panA1.add(getUnitDisplay()); getUnitDisplayDialog().setVisible(false); @@ -1677,7 +1674,7 @@ public void setUnitDisplayLocation(boolean visible) { } else { switch (GUIP.getUnitDisplayLocaton()) { case 0: - panA1.add(bvc); + panA1.add(boardViewsContainer.getPanel()); panA1.setVisible(true); getUnitDisplayDialog().add(getUnitDisplay(), BorderLayout.CENTER); getUnitDisplayDialog().setVisible(visible); @@ -1686,7 +1683,7 @@ public void setUnitDisplayLocation(boolean visible) { hideEmptyPanel(panA2, splitPaneA, 1.0); break; case 1: - panA1.add(bvc); + panA1.add(boardViewsContainer.getPanel()); panA1.setVisible(true); panA2.add(getUnitDisplay()); getUnitDisplayDialog().setVisible(false); @@ -1708,7 +1705,7 @@ public void setMiniReportLocation(boolean visible) { if (GUIP.getDockOnLeft()) { switch (GUIP.getMiniReportLocaton()) { case 0: - panA2.add(bvc); + panA2.add(boardViewsContainer.getPanel()); panA2.setVisible(true); getMiniReportDisplayDialog().add(getMiniReportDisplay(), BorderLayout.CENTER); getMiniReportDisplayDialog().setVisible(visible); @@ -1716,7 +1713,7 @@ public void setMiniReportLocation(boolean visible) { hideEmptyPanel(panA1, splitPaneA, 0.0); break; case 1: - panA2.add(bvc); + panA2.add(boardViewsContainer.getPanel()); panA2.setVisible(true); panA1.add(getMiniReportDisplay()); getMiniReportDisplayDialog().setVisible(false); @@ -1727,7 +1724,7 @@ public void setMiniReportLocation(boolean visible) { } else { switch (GUIP.getMiniReportLocaton()) { case 0: - panA1.add(bvc); + panA1.add(boardViewsContainer.getPanel()); panA1.setVisible(true); getMiniReportDisplayDialog().add(getMiniReportDisplay(), BorderLayout.CENTER); getMiniReportDisplayDialog().setVisible(visible); @@ -1735,7 +1732,7 @@ public void setMiniReportLocation(boolean visible) { hideEmptyPanel(panA2, splitPaneA, 1.0); break; case 1: - panA1.add(bvc); + panA1.add(boardViewsContainer.getPanel()); panA1.setVisible(true); panA2.add(getMiniReportDisplay()); getMiniReportDisplayDialog().setVisible(false); diff --git a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java index 1eae470aea5..885b42332fc 100644 --- a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java +++ b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java @@ -491,9 +491,9 @@ private IClientGUI getClientGUI(GameType gameType, IClient client, MegaMekContro return new BFGameManager(); */ case SBF: - return new SBFClientGUI(client, controller); + return new SBFClientGUI((SBFClient) client, controller); default: - return new ClientGUI(client, controller); + return new ClientGUI((Client) client, controller); } } @@ -910,7 +910,7 @@ void connectBot() { client = Princess.createPrincess(bcd.getBotName(), cd.getServerAddress(), cd.getPort(), bcd.getBehaviorSettings()); client.getIGame().addGameListener(new BotGUI(frame, (BotClient) client)); - ClientGUI gui = new ClientGUI(client, controller); + ClientGUI gui = new ClientGUI((Client) client, controller); controller.clientgui = gui; gui.initialize(); if (!client.connect()) { diff --git a/megamek/src/megamek/common/event/GameBoardNewEvent.java b/megamek/src/megamek/common/event/GameBoardNewEvent.java index 53526f8fdde..af907b653c9 100644 --- a/megamek/src/megamek/common/event/GameBoardNewEvent.java +++ b/megamek/src/megamek/common/event/GameBoardNewEvent.java @@ -24,20 +24,23 @@ */ public class GameBoardNewEvent extends GameEvent { private static final long serialVersionUID = -4444092727458493689L; - protected Board oldBoard; - protected Board newBoard; + + private final Board oldBoard; + private final Board newBoard; + private final int boardId; /** * Constructs the new event with the specified old/new board objects - * + * * @param source The event source * @param oldBoard old game board * @param newBoard new game board */ - public GameBoardNewEvent(Object source, Board oldBoard, Board newBoard) { + public GameBoardNewEvent(Object source, Board oldBoard, Board newBoard, int boardId) { super(source); this.oldBoard = oldBoard; this.newBoard = newBoard; + this.boardId = boardId; } /** @@ -63,4 +66,9 @@ public void fireEvent(GameListener gl) { public String getEventName() { return "New Board"; } + + /** @return The ID of both the old and new Board. */ + public int getBoardId() { + return boardId; + } } From e6d56088f1970d8e008c17751820fe27105d4842 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 9 May 2024 12:15:53 +0200 Subject: [PATCH 03/11] corrections --- .../megamek/client/ui/swing/ClientGUI.java | 26 ++++++------------- .../common/event/GameBoardNewEvent.java | 9 +------ .../strategicBattleSystems/SBFGame.java | 4 +-- 3 files changed, 11 insertions(+), 28 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java index 743ba97d66d..968a07b1e6a 100644 --- a/megamek/src/megamek/client/ui/swing/ClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java @@ -452,15 +452,6 @@ public void systemMessage(String message) { cb2.addChatMessage(Messages.getString("ChatterBox.MegaMek") + " " + message); } - /** - * Initializes a number of things about this frame. - */ - protected void initializeFrame() { - super.initializeFrame(); - menuBar = new CommonMenuBar(getClient()); - frame.setJMenuBar(menuBar); - } - /** * Lays out the frame by setting this Client object to take up the full * frame display area. @@ -489,7 +480,7 @@ private void initializeSpriteHandlers() { @Override public void initialize() { menuBar = CommonMenuBar.getMenuBarForGame(); - + frame.setJMenuBar(menuBar); initializeFrame(); super.initialize(); try { @@ -502,6 +493,13 @@ public void initialize() { bv.addOverlay(new TurnDetailsOverlay(bv)); bv.getPanel().setPreferredSize(clientGuiPanel.getSize()); bv.setTooltipProvider(new TWBoardViewTooltip(client.getGame(), this, bv)); + cb2 = new ChatterBox2(this, bv, controller); + bv.addOverlay(cb2); + bv.getPanel().addKeyListener(cb2); + offBoardOverlay = new OffBoardTargetOverlay(this); + bv.addOverlay(new UnitOverview(this)); + bv.addOverlay(offBoardOverlay); + boardViewsContainer.setName(CG_BOARDVIEW); boardViewsContainer.updateMapTabs(); initializeSpriteHandlers(); @@ -534,18 +532,10 @@ public void initialize() { layoutFrame(); menuBar.addActionListener(this); - cb2 = new ChatterBox2(this, bv, controller); - bv.addOverlay(cb2); - bv.getPanel().addKeyListener(cb2); - offBoardOverlay = new OffBoardTargetOverlay(this); - aw = new AccessibilityWindow(this); aw.setLocation(0, 0); aw.setSize(300, 300); - bv.addOverlay(new UnitOverview(this)); - bv.addOverlay(offBoardOverlay); - setUnitDisplay(new UnitDisplay(this, controller)); getUnitDisplay().addMechDisplayListener(this); setUnitDisplayDialog(new UnitDisplayDialog(getFrame(), this)); diff --git a/megamek/src/megamek/common/event/GameBoardNewEvent.java b/megamek/src/megamek/common/event/GameBoardNewEvent.java index af907b653c9..b43a2658088 100644 --- a/megamek/src/megamek/common/event/GameBoardNewEvent.java +++ b/megamek/src/megamek/common/event/GameBoardNewEvent.java @@ -27,7 +27,6 @@ public class GameBoardNewEvent extends GameEvent { private final Board oldBoard; private final Board newBoard; - private final int boardId; /** * Constructs the new event with the specified old/new board objects @@ -36,11 +35,10 @@ public class GameBoardNewEvent extends GameEvent { * @param oldBoard old game board * @param newBoard new game board */ - public GameBoardNewEvent(Object source, Board oldBoard, Board newBoard, int boardId) { + public GameBoardNewEvent(Object source, Board oldBoard, Board newBoard) { super(source); this.oldBoard = oldBoard; this.newBoard = newBoard; - this.boardId = boardId; } /** @@ -66,9 +64,4 @@ public void fireEvent(GameListener gl) { public String getEventName() { return "New Board"; } - - /** @return The ID of both the old and new Board. */ - public int getBoardId() { - return boardId; - } } diff --git a/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java b/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java index 18dd09f153c..d0d61aecee0 100644 --- a/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java +++ b/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.List; +import java.util.Map; /** * This is an SBF game's game object that holds all game information. As of 2024, this is under construction. @@ -37,11 +38,10 @@ public final class SBFGame extends AbstractGame implements PlanetaryConditionsUs private final GameOptions options = new GameOptions(); //TODO: SBFGameOptions() private GamePhase phase = GamePhase.UNKNOWN; + private GamePhase lastPhase = GamePhase.UNKNOWN; private final PlanetaryConditions planetaryConditions = new PlanetaryConditions(); private final SBFFullGameReport gameReport = new SBFFullGameReport(); - private GamePhase lastPhase = GamePhase.UNKNOWN; - @Override public GameTurn getTurn() { return null; From a577dec04b20e1272c5aad46b55945bba40b4b29 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 9 May 2024 17:50:41 +0200 Subject: [PATCH 04/11] add basic GUI processing to SBFClientGUI --- .../megamek/client/ui/swing/MegaMekGUI.java | 1 - .../megamek/client/ui/swing/SBFClientGUI.java | 388 +++++++++++++++++- .../ui/swing/SBFClientGUIGameListener.java | 51 +++ megamek/src/megamek/common/AbstractGame.java | 6 +- megamek/src/megamek/common/IGame.java | 7 + .../strategicBattleSystems/SBFGame.java | 1 + 6 files changed, 441 insertions(+), 13 deletions(-) create mode 100644 megamek/src/megamek/client/ui/swing/SBFClientGUIGameListener.java diff --git a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java index 9f15530b540..d3b7f19ae84 100644 --- a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java +++ b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java @@ -46,7 +46,6 @@ import megamek.codeUtilities.StringUtility; import megamek.common.*; import megamek.common.annotations.Nullable; -import megamek.common.enums.GamePhase; import megamek.common.options.IBasicOption; import megamek.common.options.IOption; import megamek.common.preference.IPreferenceChangeListener; diff --git a/megamek/src/megamek/client/ui/swing/SBFClientGUI.java b/megamek/src/megamek/client/ui/swing/SBFClientGUI.java index 33e751e1f52..6160d6203bb 100644 --- a/megamek/src/megamek/client/ui/swing/SBFClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/SBFClientGUI.java @@ -18,25 +18,104 @@ */ package megamek.client.ui.swing; -import megamek.client.IClient; import megamek.client.SBFClient; +import megamek.client.event.BoardViewListener; +import megamek.client.ui.Messages; import megamek.client.ui.swing.util.MegaMekController; +import megamek.client.ui.swing.util.UIUtil; import megamek.common.InGameObject; +import megamek.common.enums.GamePhase; +import megamek.common.event.GameListener; +import megamek.common.util.Distractable; +import javax.swing.*; import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.HashMap; +import java.util.Map; -public class SBFClientGUI extends AbstractClientGUI { +public class SBFClientGUI extends AbstractClientGUI implements ActionListener { + + public static final String CG_BOARDVIEW = "BoardView"; + public static final String CG_CHATLOUNGE = "ChatLounge"; + public static final String CG_STARTINGSCENARIO = "JLabel-StartingScenario"; + public static final String CG_EXCHANGE = "JLabel-Exchange"; + public static final String CG_SELECTARTYAUTOHITHEXDISPLAY = "SelectArtyAutoHitHexDisplay"; + public static final String CG_DEPLOYMINEFIELDDISPLAY = "DeployMinefieldDisplay"; + public static final String CG_DEPLOYMENTDISPLAY = "DeploymentDisplay"; + public static final String CG_TARGETINGPHASEDISPLAY = "TargetingPhaseDisplay"; + public static final String CG_PREMOVEMENTDISPLAY = "PremovementDisplay"; + public static final String CG_MOVEMENTDISPLAY = "MovementDisplay"; + public static final String CG_OFFBOARDDISPLAY = "OffboardDisplay"; + public static final String CG_PREFIRING = "Prefiring"; + public static final String CG_FIRINGDISPLAY = "FiringDisplay"; + public static final String CG_POINTBLANKSHOTDISPLAY = "PointblankShotDisplay"; + public static final String CG_PHYSICALDISPLAY = "PhysicalDisplay"; + public static final String CG_REPORTDISPLAY = "ReportDisplay"; + public static final String CG_DEFAULT = "JLabel-Default"; private final SBFClient client; - public SBFClientGUI(IClient client, MegaMekController c) { + protected JComponent curPanel; + private JPanel panTop; + + /** + * The JPanel containing the main display area. + */ + private final JPanel panMain = new JPanel(); + + /** + * The CardLayout of the main display area. + */ + private final CardLayout cardsMain = new CardLayout(); + + /** + * Map each phase to the name of the card for the secondary area. + */ + private final Map secondaryNames = new HashMap<>(); + + /** + * The JPanel containing the secondary display area. + */ + private final JPanel panSecondary = new JPanel(); + + + private ReportDisplay reportDisply; + + private StatusBarPhaseDisplay currPhaseDisplay; + + /** + * The CardLayout of the secondary display area. + */ + private final CardLayout cardsSecondary = new CardLayout(); + + /** + * Map phase component names to phase component objects. + */ + private final Map phaseComponents = new HashMap<>(); + + /** + * Map each phase to the name of the card for the main display area. + */ + private final Map mainNames = new HashMap<>(); + + private final GameListener gameListener = new SBFClientGUIGameListener(this); + private final CommonMenuBar menuBar = CommonMenuBar.getMenuBarForGame(); + + public SBFClientGUI(SBFClient client, MegaMekController c) { super(client); - if (!(client instanceof SBFClient)) { - throw new IllegalArgumentException("SBF ClientGUI must use SBF Client!"); - } - this.client = (SBFClient) client; + this.client = client; frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(new UnderConstructionPanel(), BorderLayout.CENTER); + frame.setJMenuBar(menuBar); + menuBar.addActionListener(this); + } + + @Override + public void initialize() { + super.initialize(); + client.getIGame().addGameListener(gameListener); frame.setVisible(true); } @@ -46,8 +125,303 @@ protected boolean saveGame() { return true; } + @Override + public void die() { + super.die(); + client.getIGame().removeGameListener(gameListener); + } + @Override public InGameObject getSelectedUnit() { return null; } + + protected void switchPanel(GamePhase phase) { + // Clear the old panel's listeners. +// if (curPanel instanceof BoardViewListener) { +// bv.removeBoardViewListener((BoardViewListener) curPanel); +// } + + if (curPanel instanceof ActionListener) { + menuBar.removeActionListener((ActionListener) curPanel); + } + + if (curPanel instanceof Distractable) { + ((Distractable) curPanel).setIgnoringEvents(true); + } + + // Get the new panel. + String name = String.valueOf(phase); + curPanel = phaseComponents.get(name); + if (curPanel == null) { + curPanel = initializePanel(phase); + } + + // Handle phase-specific items. + switch (phase) { + case LOUNGE: +// // reset old report tabs and images, if any +// ChatLounge cl = (ChatLounge) phaseComponents.get(String.valueOf(GamePhase.LOUNGE)); +// cb.setDoneButton(cl.butDone); +// cl.setBottom(cb.getComponent()); +// getBoardView().getTilesetManager().reset(); + break; + case POINTBLANK_SHOT: + case SET_ARTILLERY_AUTOHIT_HEXES: + case DEPLOY_MINEFIELDS: + case DEPLOYMENT: + case TARGETING: + case PREMOVEMENT: + case MOVEMENT: + case OFFBOARD: + case PREFIRING: + case FIRING: + case PHYSICAL: + case INITIATIVE_REPORT: + case TARGETING_REPORT: + case MOVEMENT_REPORT: + case OFFBOARD_REPORT: + case FIRING_REPORT: + case PHYSICAL_REPORT: + case END_REPORT: + case VICTORY: + default: + break; + } + +// maybeShowMinimap(); +// maybeShowUnitDisplay(); +// maybeShowForceDisplay(); +// maybeShowMiniReport(); +// maybeShowPlayerList(); + + cardsMain.show(panMain, mainNames.get(name)); + String secondaryToShow = secondaryNames.get(name); + // only show the secondary component if there is one to show + if (secondaryToShow != null) { + panSecondary.setVisible(true); + cardsSecondary.show(panSecondary, secondaryNames.get(name)); + } else { + // otherwise, hide the panel + panSecondary.setVisible(false); + } + + // Set the new panel's listeners +// if (curPanel instanceof BoardViewListener) { +// bv.addBoardViewListener((BoardViewListener) curPanel); +// } + + if (curPanel instanceof ActionListener) { + menuBar.addActionListener((ActionListener) curPanel); + } + + if (curPanel instanceof Distractable) { + ((Distractable) curPanel).setIgnoringEvents(false); + } + + // Make the new panel the focus, if the Client option says so +// if (GUIP.getFocus() && !(client instanceof BotClient)) { +// curPanel.requestFocus(); +// } + } + + private JComponent initializePanel(GamePhase phase) { + // Create the components for this phase. + String name = String.valueOf(phase); + JComponent component = new ReceivingGameDataPanel(); + String secondary = null; + String main; + switch (phase) { + case LOUNGE: +// component = new ChatLounge(this); +// chatlounge = (ChatLounge) component; + main = CG_CHATLOUNGE; + component.setName(main); + panMain.add(component, main); + break; + case STARTING_SCENARIO: + component = new JLabel(Messages.getString("ClientGUI.StartingScenario")); + UIUtil.scaleComp(component, UIUtil.FONT_SCALE1); + main = CG_STARTINGSCENARIO; + component.setName(main); + panMain.add(component, main); + break; + case EXCHANGE: +// chatlounge.killPreviewBV(); + component = new ReceivingGameDataPanel(); + UIUtil.scaleComp(component, UIUtil.FONT_SCALE1); + main = CG_EXCHANGE; + component.setName(main); + panMain.add(component, main); + break; + case SET_ARTILLERY_AUTOHIT_HEXES: +// component = new SelectArtyAutoHitHexDisplay(this); + main = CG_BOARDVIEW; + secondary = CG_SELECTARTYAUTOHITHEXDISPLAY; + component.setName(secondary); + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = (StatusBarPhaseDisplay) component; + panSecondary.add(component, secondary); + break; + case DEPLOY_MINEFIELDS: +// component = new DeployMinefieldDisplay(this); + main = CG_BOARDVIEW; + secondary = CG_DEPLOYMINEFIELDDISPLAY; + component.setName(secondary); + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = (StatusBarPhaseDisplay) component; + panSecondary.add(component, secondary); + break; + case DEPLOYMENT: +// component = new DeploymentDisplay(this); + main = CG_BOARDVIEW; + secondary = CG_DEPLOYMENTDISPLAY; + component.setName(secondary); + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = (StatusBarPhaseDisplay) component; + panSecondary.add(component, secondary); + break; + case TARGETING: +// component = new TargetingPhaseDisplay(this, false); +// ((TargetingPhaseDisplay) component).initializeListeners(); + main = CG_BOARDVIEW; + secondary = CG_TARGETINGPHASEDISPLAY; + component.setName(secondary); + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = (StatusBarPhaseDisplay) component; + panSecondary.add(component, secondary); +// offBoardOverlay.setTargetingPhaseDisplay((TargetingPhaseDisplay) component); + break; + case PREMOVEMENT: +// component = new PrephaseDisplay(this, GamePhase.PREMOVEMENT); +// ((PrephaseDisplay) component).initializeListeners(); + main = CG_BOARDVIEW; + secondary = CG_PREMOVEMENTDISPLAY; + component.setName(secondary); + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = (StatusBarPhaseDisplay) component; + panSecondary.add(component, secondary); + break; + case MOVEMENT: +// component = new MovementDisplay(this); + main = CG_BOARDVIEW; + secondary = CG_MOVEMENTDISPLAY; + component.setName(secondary); + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = (StatusBarPhaseDisplay) component; + panSecondary.add(component, secondary); + break; + case OFFBOARD: +// component = new TargetingPhaseDisplay(this, true); +// ((TargetingPhaseDisplay) component).initializeListeners(); + main = CG_BOARDVIEW; + secondary = CG_OFFBOARDDISPLAY; + component.setName(secondary); + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = (StatusBarPhaseDisplay) component; + panSecondary.add(component, secondary); + break; + case PREFIRING: +// component = new PrephaseDisplay(this, GamePhase.PREFIRING); +// ((PrephaseDisplay) component).initializeListeners(); + main = CG_BOARDVIEW; + secondary = CG_PREFIRING; + component.setName(secondary); + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = (StatusBarPhaseDisplay) component; + panSecondary.add(component, secondary); + break; + case FIRING: +// component = new FiringDisplay(this); + main = CG_BOARDVIEW; + secondary = CG_FIRINGDISPLAY; + component.setName(secondary); + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = (StatusBarPhaseDisplay) component; + panSecondary.add(component, secondary); + break; + case POINTBLANK_SHOT: +// component = new PointblankShotDisplay(this); + main = CG_BOARDVIEW; + secondary = CG_POINTBLANKSHOTDISPLAY; + component.setName(secondary); + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = (StatusBarPhaseDisplay) component; + panSecondary.add(component, secondary); + break; + case PHYSICAL: +// component = new PhysicalDisplay(this); + main = CG_BOARDVIEW; + secondary = CG_PHYSICALDISPLAY; + component.setName(secondary); + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = (StatusBarPhaseDisplay) component; + panSecondary.add(component, secondary); + break; + case INITIATIVE_REPORT: + case TARGETING_REPORT: + case MOVEMENT_REPORT: + case OFFBOARD_REPORT: + case FIRING_REPORT: + case PHYSICAL_REPORT: + case END_REPORT: + case VICTORY: + main = CG_BOARDVIEW; + secondary = CG_REPORTDISPLAY; + if (reportDisply == null) { +// reportDisply = new ReportDisplay(this); +// reportDisply.setName(secondary); + } + if (!mainNames.containsValue(main)) { + panMain.add(panTop, main); + } + currPhaseDisplay = reportDisply; + component = reportDisply; + if (!secondaryNames.containsValue(secondary)) { + panSecondary.add(reportDisply, secondary); + } + break; + default: + component = new WaitingForServerPanel(); + main = CG_DEFAULT; + secondary = main; + component.setName(main); + panMain.add(main, component); + break; + } + phaseComponents.put(name, component); + mainNames.put(name, main); + if (secondary != null) { + secondaryNames.put(name, secondary); + } + + return component; + } + + @Override + public void actionPerformed(ActionEvent e) { + + } } diff --git a/megamek/src/megamek/client/ui/swing/SBFClientGUIGameListener.java b/megamek/src/megamek/client/ui/swing/SBFClientGUIGameListener.java new file mode 100644 index 00000000000..7cda42c911e --- /dev/null +++ b/megamek/src/megamek/client/ui/swing/SBFClientGUIGameListener.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MegaMek. + * + * MegaMek is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MegaMek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MegaMek. If not, see . + */ + +package megamek.client.ui.swing; + +import megamek.common.event.*; + +public class SBFClientGUIGameListener extends GameListenerAdapter { + + private final SBFClientGUI clientGUI; + + public SBFClientGUIGameListener(SBFClientGUI clientGUI) { + this.clientGUI = clientGUI; + } + + @Override + public void gamePhaseChange(GamePhaseChangeEvent e) { +// // This is a really lame place for this, but I couldn't find a +// // better one without making massive changes (which didn't seem +// // worth it for one little feature). +// if (bv.getLocalPlayer() != client.getLocalPlayer()) { +// // The adress based comparison is somewhat important. +// // Use of the /reset command can cause the player to get reset, +// // and the equals function of Player isn't powerful enough. +// bv.setLocalPlayer(client.getLocalPlayer()); +// } +// // Make sure the ChatterBox starts out deactived. +// bv.setChatterBoxActive(false); +// + // Swap to this phase's panel. + clientGUI.switchPanel(e.getNewPhase()); +// clientGUI.menuBar.setPhase(phase); +// clientGUI.cb.moveToEnd(); + } +} diff --git a/megamek/src/megamek/common/AbstractGame.java b/megamek/src/megamek/common/AbstractGame.java index f9fe25bc721..b5968b76fae 100644 --- a/megamek/src/megamek/common/AbstractGame.java +++ b/megamek/src/megamek/common/AbstractGame.java @@ -134,11 +134,7 @@ public void addGameListener(GameListener listener) { gameListeners.add(listener); } - /** - * Removes the specified game listener. - * - * @param listener the game listener. - */ + @Override public void removeGameListener(GameListener listener) { // Since gameListeners is transient, it could be null if (gameListeners == null) { diff --git a/megamek/src/megamek/common/IGame.java b/megamek/src/megamek/common/IGame.java index 07ce46012ab..5d57df52e89 100644 --- a/megamek/src/megamek/common/IGame.java +++ b/megamek/src/megamek/common/IGame.java @@ -89,6 +89,13 @@ default void receivePhase(GamePhase phase) { */ void addGameListener(GameListener listener); + /** + * Removes the specified game listener. + * + * @param listener the game listener. + */ + void removeGameListener(GameListener listener); + /** * @return Whether there is an active claim for victory. */ diff --git a/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java b/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java index d0d61aecee0..ac2baa31999 100644 --- a/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java +++ b/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java @@ -22,6 +22,7 @@ import megamek.common.alphaStrike.AlphaStrikeElement; import megamek.common.annotations.Nullable; import megamek.common.enums.GamePhase; +import megamek.common.event.GameListener; import megamek.common.options.GameOptions; import megamek.common.options.OptionsConstants; import megamek.common.planetaryconditions.PlanetaryConditions; From c4a4f3b919de04e6f5157bb44a521be264095f86 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 9 May 2024 21:42:49 +0200 Subject: [PATCH 05/11] add basic GUI processing to SBFClientGUI --- .../src/megamek/client/AbstractClient.java | 24 +-- megamek/src/megamek/client/Client.java | 10 +- megamek/src/megamek/client/IClient.java | 13 +- megamek/src/megamek/client/SBFClient.java | 14 +- .../megamek/client/bot/princess/Princess.java | 5 - .../client/ui/swing/AbstractClientGUI.java | 4 - .../client/ui/swing/AbstractPhaseDisplay.java | 183 ++++------------ .../client/ui/swing/BoardViewsContainer.java | 2 +- .../megamek/client/ui/swing/ClientGUI.java | 34 ++- .../megamek/client/ui/swing/IClientGUI.java | 4 +- .../megamek/client/ui/swing/MegaMekGUI.java | 9 +- .../client/ui/swing/MovementDisplay.java | 2 +- .../megamek/client/ui/swing/SBFClientGUI.java | 202 +++++++----------- .../ui/swing/StartingScenarioPanel.java | 51 +++++ .../ui/swing/StatusBarPhaseDisplay.java | 2 + .../client/ui/swing/lobby/ChatLounge.java | 9 +- .../client/ui/swing/util/TurnTimer.java | 10 +- megamek/src/megamek/common/Game.java | 5 - .../strategicBattleSystems/SBFGame.java | 8 + 19 files changed, 246 insertions(+), 345 deletions(-) create mode 100644 megamek/src/megamek/client/ui/swing/StartingScenarioPanel.java diff --git a/megamek/src/megamek/client/AbstractClient.java b/megamek/src/megamek/client/AbstractClient.java index f57ad9e6d1a..a1905fde4ec 100644 --- a/megamek/src/megamek/client/AbstractClient.java +++ b/megamek/src/megamek/client/AbstractClient.java @@ -156,7 +156,7 @@ protected void disconnected() { } if (!host.equals(MMConstants.LOCALHOST)) { - getIGame().fireGameEvent(new GamePlayerDisconnectedEvent(this, getLocalPlayer())); + getGame().fireGameEvent(new GamePlayerDisconnectedEvent(this, getLocalPlayer())); } } } @@ -185,7 +185,7 @@ public void sendNextPlayer() { * Sends the info associated with the local player. */ public void sendPlayerInfo() { - send(new Packet(PacketCommand.PLAYER_UPDATE, getIGame().getPlayer(localPlayerNumber))); + send(new Packet(PacketCommand.PLAYER_UPDATE, getGame().getPlayer(localPlayerNumber))); } /** @@ -217,9 +217,9 @@ protected void receivePlayerInfo(Packet c) { int pindex = c.getIntValue(0); Player newPlayer = (Player) c.getObject(1); if (!playerExists(newPlayer.getId())) { - getIGame().addPlayer(pindex, newPlayer); + getGame().addPlayer(pindex, newPlayer); } else { - getIGame().setPlayer(pindex, newPlayer); + getGame().setPlayer(pindex, newPlayer); } } @@ -301,11 +301,11 @@ protected synchronized void checkDuplicateNamesDuringAdd(Entity entity) { protected void receiveUnitReplace(Packet packet) { @SuppressWarnings(value = "unchecked") List forces = (List) packet.getObject(1); - forces.forEach(force -> getIGame().getForces().replace(force.getId(), force)); + forces.forEach(force -> getGame().getForces().replace(force.getId(), force)); @SuppressWarnings(value = "unchecked") List units = (List) packet.getObject(0); - getIGame().replaceUnits(units); + getGame().replaceUnits(units); } /** @@ -441,25 +441,25 @@ protected boolean handleGameIndependentPacket(Packet packet) { Player player = getPlayer(packet.getIntValue(0)); if (player != null) { player.setDone(packet.getBooleanValue(1)); - getIGame().fireGameEvent(new GamePlayerChangeEvent(player, player)); + getGame().fireGameEvent(new GamePlayerChangeEvent(player, player)); } break; case PLAYER_REMOVE: bots.values().removeIf(bot -> bot.localPlayerNumber == packet.getIntValue(0)); - getIGame().removePlayer(packet.getIntValue(0)); + getGame().removePlayer(packet.getIntValue(0)); break; case CHAT: possiblyWriteToLog((String) packet.getObject(0)); - getIGame().fireGameEvent(new GamePlayerChatEvent(this, null, (String) packet.getObject(0))); + getGame().fireGameEvent(new GamePlayerChatEvent(this, null, (String) packet.getObject(0))); break; case ENTITY_ADD: receiveUnitReplace(packet); break; case SENDING_BOARD: - getIGame().receiveBoards((Map) packet.getObject(0)); + getGame().receiveBoards((Map) packet.getObject(0)); break; case ROUND_UPDATE: - getIGame().setCurrentRound(packet.getIntValue(0)); + getGame().setCurrentRound(packet.getIntValue(0)); break; case PHASE_CHANGE: changePhase((GamePhase) packet.getObject(0)); @@ -485,7 +485,7 @@ protected void possiblyWriteToLog(String message) { * Changes the game phase, and the displays that go along with it. */ public void changePhase(GamePhase phase) { - getIGame().receivePhase(phase); + getGame().receivePhase(phase); switch (phase) { case STARTING_SCENARIO: case EXCHANGE: diff --git a/megamek/src/megamek/client/Client.java b/megamek/src/megamek/client/Client.java index eaa3718a956..ab3160a393c 100644 --- a/megamek/src/megamek/client/Client.java +++ b/megamek/src/megamek/client/Client.java @@ -99,16 +99,10 @@ public Client(String name, String host, int port) { } } - @Override - public IGame getIGame() { - return game; - } - public Game getGame() { return game; } - /** * Get hexes designated for automatic artillery hits. */ @@ -205,9 +199,7 @@ public void changePhase(GamePhase phase) { } } - /** - * is it my turn? - */ + @Override public boolean isMyTurn() { if (getGame().getPhase().isSimultaneous(getGame())) { return game.getTurnForPlayer(localPlayerNumber) != null; diff --git a/megamek/src/megamek/client/IClient.java b/megamek/src/megamek/client/IClient.java index 747e972632a..e956147a0d0 100644 --- a/megamek/src/megamek/client/IClient.java +++ b/megamek/src/megamek/client/IClient.java @@ -56,26 +56,31 @@ public interface IClient { * * @return The game of this client */ - IGame getIGame(); + IGame getGame(); /** @return The in-game object associated with the given ID. */ default Optional getInGameObject(int id) { - return getIGame().getInGameObject(id); + return getGame().getInGameObject(id); } /** @return A list of all in-game objects of the game. This list is copied and may be safely modified. */ default List getInGameObjects() { - return getIGame().getInGameObjects(); + return getGame().getInGameObjects(); } /** @return The player with the given ID, or null if there is no such player. */ default Player getPlayer(int id) { - return getIGame().getPlayer(id); + return getGame().getPlayer(id); } /** @return The ID of the player playing at this Client. */ int getLocalPlayerNumber(); + /** + * @return True when the currently active turn is a turn for the local player + */ + boolean isMyTurn(); + /** * Sets the ID of the player playing at this Client. * // TODO : only used by AddBotUtil -> could be included in a bot's constructor and removed here diff --git a/megamek/src/megamek/client/SBFClient.java b/megamek/src/megamek/client/SBFClient.java index dc06b47c7dc..1716f486130 100644 --- a/megamek/src/megamek/client/SBFClient.java +++ b/megamek/src/megamek/client/SBFClient.java @@ -18,20 +18,13 @@ */ package megamek.client; -import megamek.client.ui.swing.tooltip.PilotToolTip; -import megamek.client.ui.swing.util.UIUtil; import megamek.common.*; import megamek.common.net.packets.Packet; import megamek.common.strategicBattleSystems.SBFGame; -import megamek.common.util.ImageUtil; import org.apache.logging.log4j.LogManager; -import java.awt.*; -import java.awt.image.BufferedImage; import java.util.*; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; /** @@ -60,10 +53,15 @@ public SBFClient(String name, String host, int port) { } @Override - public IGame getIGame() { + public SBFGame getGame() { return game; } + @Override + public boolean isMyTurn() { + return (game.getTurn() != null) && game.getTurn().isValid(localPlayerNumber, game); + } + @Override @SuppressWarnings("unchecked") protected boolean handleGameSpecificPacket(Packet packet) { diff --git a/megamek/src/megamek/client/bot/princess/Princess.java b/megamek/src/megamek/client/bot/princess/Princess.java index e60758f55a8..4a1b1151a5f 100644 --- a/megamek/src/megamek/client/bot/princess/Princess.java +++ b/megamek/src/megamek/client/bot/princess/Princess.java @@ -1610,11 +1610,6 @@ protected void initMovement() { } } - @Override - public Game getGame() { - return game; - } - @Override public void initialize() { try { diff --git a/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java b/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java index 807e764f063..402d3081445 100644 --- a/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/AbstractClientGUI.java @@ -64,10 +64,6 @@ public AbstractClientGUI(IClient iClient) { initializeFrame(); } - public IClient getIClient() { - return iClient; - } - @Override public JFrame getFrame() { return frame; diff --git a/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java b/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java index 1b3a311016f..654b0492ae5 100644 --- a/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java +++ b/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java @@ -18,17 +18,12 @@ import megamek.client.ui.swing.util.KeyCommandBind; import megamek.client.ui.swing.util.UIUtil; import megamek.client.ui.swing.widget.*; -import megamek.common.Configuration; import megamek.common.event.*; import megamek.common.util.Distractable; import megamek.common.util.DistractableAdapter; -import megamek.common.util.fileUtils.MegaMekFile; -import org.apache.logging.log4j.LogManager; import javax.swing.*; import java.awt.event.ActionEvent; -import java.io.File; -import java.net.URI; import static megamek.client.ui.swing.util.UIUtil.guiScaledFontHTML; @@ -37,41 +32,19 @@ public abstract class AbstractPhaseDisplay extends SkinnedJPanel implements private static final long serialVersionUID = 4421205210788230341L; public static final int DONE_BUTTON_WIDTH = 160; - // Distraction implementation. - protected DistractableAdapter distracted = new DistractableAdapter(); + protected DistractableAdapter distracted = new DistractableAdapter(); protected MegamekButton butDone; + protected IClientGUI clientgui; - protected ClientGUI clientgui; - - ImageIcon backgroundIcon = null; - - protected AbstractPhaseDisplay(ClientGUI cg) { + protected AbstractPhaseDisplay(IClientGUI cg) { this(cg, SkinSpecification.UIComponents.PhaseDisplay.getComp(), SkinSpecification.UIComponents.PhaseDisplayDoneButton.getComp()); } - protected AbstractPhaseDisplay(ClientGUI cg, String borderSkinComp, - String buttonSkinComp) { + protected AbstractPhaseDisplay(IClientGUI cg, String borderSkinComp, String buttonSkinComp) { super(borderSkinComp, 0); - this.clientgui = cg; - SkinSpecification pdSkinSpec = SkinXMLHandler.getSkin(borderSkinComp); - - try { - if (!pdSkinSpec.backgrounds.isEmpty()) { - File file = new MegaMekFile(Configuration.widgetsDir(), - pdSkinSpec.backgrounds.get(0)).getFile(); - URI imgURL = file.toURI(); - if (!file.exists()) { - LogManager.getLogger().error("PhaseDisplay icon doesn't exist: " + file.getAbsolutePath()); - } else { - backgroundIcon = new ImageIcon(imgURL.toURL()); - } - } - } catch (Exception e) { - LogManager.getLogger().error("Error loading PhaseDisplay background image!", e); - } - + clientgui = cg; setBorder(new MegamekBorder(borderSkinComp)); butDone = new MegamekButton("DONE", buttonSkinComp); String f = guiScaledFontHTML(UIUtil.uiLightViolet()) + KeyCommandBind.getDesc(KeyCommandBind.DONE)+ ""; @@ -92,13 +65,12 @@ public void actionPerformed(ActionEvent e) { ready(); // When the turn is ended, we could miss a key release event // This will ensure no repeating keys are stuck down - clientgui.controller.stopAllRepeating(); + MegaMekGUI.getKeyDispatcher().stopAllRepeating(); } } }); - clientgui.controller.registerCommandAction(KeyCommandBind.DONE, this::shouldPerformKeyCommands, - this::ready); + MegaMekGUI.getKeyDispatcher().registerCommandAction(KeyCommandBind.DONE, this::shouldPerformKeyCommands, this::ready); } } @@ -106,181 +78,110 @@ private boolean shouldPerformKeyCommands() { return ((clientgui.getClient().isMyTurn() || (clientgui.getClient().getGame().getTurn() == null) || (clientgui.getClient().getGame().getPhase().isReport()))) - && !clientgui.getBoardView().getChatterBoxActive() + && !clientgui.shouldIgnoreHotKeys() && !isIgnoringEvents() && isVisible() && butDone.isEnabled(); } - /** - * Determine if the listener is currently distracted. - * - * @return true if the listener is ignoring events. - */ @Override public boolean isIgnoringEvents() { return distracted.isIgnoringEvents(); } - /** - * Specify if the listener should be distracted. - * - * @param distracted - * true if the listener should ignore events - * false if the listener should pay attention - * again. Events that occurred while the listener was distracted - * NOT going to be processed. - */ @Override public void setIgnoringEvents(boolean distracted) { this.distracted.setIgnoringEvents(distracted); } - // + public void ready() { } + + public IClientGUI getClientgui() { + return clientgui; + } + // BoardListener - // + @Override - public void hexMoused(BoardViewEvent b) { - //noaction default - } + public void hexMoused(BoardViewEvent b) { } @Override - public void hexSelected(BoardViewEvent b) { - //noaction default - } + public void hexSelected(BoardViewEvent b) { } @Override - public void hexCursor(BoardViewEvent b) { - //noaction default - } + public void hexCursor(BoardViewEvent b) { } @Override - public void boardHexHighlighted(BoardViewEvent b) { - //noaction default - } + public void boardHexHighlighted(BoardViewEvent b) { } @Override - public void firstLOSHex(BoardViewEvent b) { - //noaction default - } + public void firstLOSHex(BoardViewEvent b) { } @Override - public void secondLOSHex(BoardViewEvent b) { - //noaction default - } + public void secondLOSHex(BoardViewEvent b) { } @Override - public void finishedMovingUnits(BoardViewEvent b) { - //noaction default - } + public void finishedMovingUnits(BoardViewEvent b) { } @Override - public void unitSelected(BoardViewEvent b) { - //noaction default - } + public void unitSelected(BoardViewEvent b) { } // GameListener - // @Override - public void gamePlayerConnected(GamePlayerConnectedEvent e) { - //noaction default - } + public void gamePlayerConnected(GamePlayerConnectedEvent e) { } @Override - public void gamePlayerDisconnected(GamePlayerDisconnectedEvent e) { - //noaction default - } + public void gamePlayerDisconnected(GamePlayerDisconnectedEvent e) { } @Override - public void gamePlayerChange(GamePlayerChangeEvent e) { - //noaction default - } + public void gamePlayerChange(GamePlayerChangeEvent e) { } @Override - public void gamePlayerChat(GamePlayerChatEvent e) { - //noaction default - } + public void gamePlayerChat(GamePlayerChatEvent e) { } @Override - public void gamePhaseChange(GamePhaseChangeEvent e) { - //noaction default - } + public void gamePhaseChange(GamePhaseChangeEvent e) { } @Override - public void gameTurnChange(GameTurnChangeEvent e) { - //noaction default - } + public void gameTurnChange(GameTurnChangeEvent e) { } @Override - public void gameReport(GameReportEvent e) { - //noaction default - } + public void gameReport(GameReportEvent e) { } @Override - public void gameEnd(GameEndEvent e) { - //noaction default - } + public void gameEnd(GameEndEvent e) { } @Override - public void gameBoardNew(GameBoardNewEvent e) { - //noaction default - } + public void gameBoardNew(GameBoardNewEvent e) { } @Override - public void gameBoardChanged(GameBoardChangeEvent e) { - //noaction default - } + public void gameBoardChanged(GameBoardChangeEvent e) { } @Override - public void gameSettingsChange(GameSettingsChangeEvent e) { - //noaction default - } + public void gameSettingsChange(GameSettingsChangeEvent e) { } @Override - public void gameMapQuery(GameMapQueryEvent e) { - //noaction default - } + public void gameMapQuery(GameMapQueryEvent e) { } @Override - public void gameEntityNew(GameEntityNewEvent e) { - //noaction default - } + public void gameEntityNew(GameEntityNewEvent e) { } @Override - public void gameEntityNewOffboard(GameEntityNewOffboardEvent e) { - //noaction default - } + public void gameEntityNewOffboard(GameEntityNewOffboardEvent e) { } @Override - public void gameEntityRemove(GameEntityRemoveEvent e) { - //noaction default - } + public void gameEntityRemove(GameEntityRemoveEvent e) { } @Override - public void gameEntityChange(GameEntityChangeEvent e) { - //noaction default - } + public void gameEntityChange(GameEntityChangeEvent e) { } @Override - public void gameNewAction(GameNewActionEvent e) { - //noaction default - } + public void gameNewAction(GameNewActionEvent e) { } @Override - public void gameClientFeedbackRequest(GameCFREvent evt) { - //noaction default - } + public void gameClientFeedbackRequest(GameCFREvent evt) { } @Override - public void gameVictory(GameVictoryEvent e) { - //noaction default - } - - public void ready() { - } - // needed for turn timer to add timer display to GUI - public ClientGUI getClientgui() { - return clientgui; - } + public void gameVictory(GameVictoryEvent e) { } } diff --git a/megamek/src/megamek/client/ui/swing/BoardViewsContainer.java b/megamek/src/megamek/client/ui/swing/BoardViewsContainer.java index 04e1f0672c3..d6b21ab5a31 100644 --- a/megamek/src/megamek/client/ui/swing/BoardViewsContainer.java +++ b/megamek/src/megamek/client/ui/swing/BoardViewsContainer.java @@ -104,7 +104,7 @@ private String getBoardViewTabTooltip(int boardId) { } private Board board(int id) { - return clientGUI.getIClient().getIGame().getBoard(id); + return clientGUI.getClient().getGame().getBoard(id); } private IBoardView boardView(int id) { diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java index 968a07b1e6a..86db4d96974 100644 --- a/megamek/src/megamek/client/ui/swing/ClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java @@ -82,7 +82,7 @@ import java.util.*; public class ClientGUI extends AbstractClientGUI implements BoardViewListener, - ActionListener, ComponentListener, IPreferenceChangeListener, MechDisplayListener { + ActionListener, IPreferenceChangeListener, MechDisplayListener { // region Variable Declarations public static final int WEAPON_NONE = -1; @@ -357,7 +357,7 @@ public ClientGUI(Client client, MegaMekController c) { panSecondary.setLayout(cardsSecondary); clientGuiPanel.setLayout(new BorderLayout()); - clientGuiPanel.addComponentListener(this); + clientGuiPanel.addComponentListener(resizeListener); clientGuiPanel.add(panMain, BorderLayout.CENTER); clientGuiPanel.add(panSecondary, BorderLayout.SOUTH); @@ -2519,6 +2519,11 @@ public Client getClient() { return client; } + @Override + public JComponent turnTimerComponent() { + return menuBar; + } + public Map getLocalBots() { return client.getBots(); } @@ -2712,25 +2717,12 @@ public boolean shouldIgnoreHotKeys() { || ((aw != null) && aw.isVisible()); } - @Override - public void componentHidden(ComponentEvent evt) { - - } - - @Override - public void componentMoved(ComponentEvent evt) { - - } - - @Override - public void componentResized(ComponentEvent evt) { - bv.getPanel().setPreferredSize(clientGuiPanel.getSize()); - } - - @Override - public void componentShown(ComponentEvent evt) { - - } + private final ComponentListener resizeListener = new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent evt) { + boardViewsContainer.getPanel().setPreferredSize(clientGuiPanel.getSize()); + } + }; void editBots() { var rpd = new EditBotsDialog(frame, this); diff --git a/megamek/src/megamek/client/ui/swing/IClientGUI.java b/megamek/src/megamek/client/ui/swing/IClientGUI.java index 1acd6afe404..61ca5b53eed 100644 --- a/megamek/src/megamek/client/ui/swing/IClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/IClientGUI.java @@ -51,5 +51,7 @@ public interface IClientGUI { InGameObject getSelectedUnit(); - IClient getIClient(); + IClient getClient(); + + JComponent turnTimerComponent(); } diff --git a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java index d3b7f19ae84..e1dd9c52362 100644 --- a/megamek/src/megamek/client/ui/swing/MegaMekGUI.java +++ b/megamek/src/megamek/client/ui/swing/MegaMekGUI.java @@ -98,7 +98,7 @@ public class MegaMekGUI implements IPreferenceChangeListener { private IGameManager gameManager; private CommonSettingsDialog settingsDialog; - private MegaMekController controller; + private static MegaMekController controller; public void start(boolean show) { createGUI(show); @@ -184,10 +184,13 @@ public void createController() { controller = new MegaMekController(); KeyboardFocusManager kbfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); kbfm.addKeyEventDispatcher(controller); - KeyBindParser.parseKeyBindings(controller); } + public static MegaMekController getKeyDispatcher() { + return controller; + } + /** * Display the main menu. */ @@ -908,7 +911,7 @@ void connectBot() { } client = Princess.createPrincess(bcd.getBotName(), cd.getServerAddress(), cd.getPort(), bcd.getBehaviorSettings()); - client.getIGame().addGameListener(new BotGUI(frame, (BotClient) client)); + client.getGame().addGameListener(new BotGUI(frame, (BotClient) client)); ClientGUI gui = new ClientGUI((Client) client, controller); controller.clientgui = gui; gui.initialize(); diff --git a/megamek/src/megamek/client/ui/swing/MovementDisplay.java b/megamek/src/megamek/client/ui/swing/MovementDisplay.java index 863c6a1a9a9..461bfc08299 100644 --- a/megamek/src/megamek/client/ui/swing/MovementDisplay.java +++ b/megamek/src/megamek/client/ui/swing/MovementDisplay.java @@ -2722,7 +2722,7 @@ private void updateBraceButton() { MovePath movePath = cmd; if (null == movePath) { - movePath = new MovePath(this.getClientgui().getClient().getGame(), ce()); + movePath = new MovePath(clientgui.getClient().getGame(), ce()); } if (!movePath.contains(MoveStepType.BRACE) && diff --git a/megamek/src/megamek/client/ui/swing/SBFClientGUI.java b/megamek/src/megamek/client/ui/swing/SBFClientGUI.java index 6160d6203bb..9961ea3e45e 100644 --- a/megamek/src/megamek/client/ui/swing/SBFClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/SBFClientGUI.java @@ -19,10 +19,8 @@ package megamek.client.ui.swing; import megamek.client.SBFClient; -import megamek.client.event.BoardViewListener; import megamek.client.ui.Messages; import megamek.client.ui.swing.util.MegaMekController; -import megamek.client.ui.swing.util.UIUtil; import megamek.common.InGameObject; import megamek.common.enums.GamePhase; import megamek.common.event.GameListener; @@ -30,8 +28,7 @@ import javax.swing.*; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import java.awt.event.*; import java.util.HashMap; import java.util.Map; @@ -57,8 +54,11 @@ public class SBFClientGUI extends AbstractClientGUI implements ActionListener { private final SBFClient client; + // a frame, to show stuff in + private final JPanel clientGuiPanel = new JPanel(); + protected JComponent curPanel; - private JPanel panTop; + private final JPanel panTop = new JPanel(new BorderLayout()); /** * The JPanel containing the main display area. @@ -107,15 +107,42 @@ public SBFClientGUI(SBFClient client, MegaMekController c) { super(client); this.client = client; frame.getContentPane().setLayout(new BorderLayout()); - frame.getContentPane().add(new UnderConstructionPanel(), BorderLayout.CENTER); + frame.getContentPane().add(clientGuiPanel, BorderLayout.CENTER); +// frame.getContentPane().add(new UnderConstructionPanel(), BorderLayout.CENTER); frame.setJMenuBar(menuBar); menuBar.addActionListener(this); + panMain.setLayout(cardsMain); + panSecondary.setLayout(cardsSecondary); + + panMain.add("UnderConstruction", new UnderConstructionPanel()); + + clientGuiPanel.setLayout(new BorderLayout()); + clientGuiPanel.addComponentListener(resizeListener); + clientGuiPanel.add(panMain, BorderLayout.CENTER); + clientGuiPanel.add(panSecondary, BorderLayout.SOUTH); + } + + private final ComponentListener resizeListener = new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent evt) { + boardViewsContainer.getPanel().setPreferredSize(clientGuiPanel.getSize()); + } + }; + + @Override + public SBFClient getClient() { + return client; + } + + @Override + public JComponent turnTimerComponent() { + return menuBar; } @Override public void initialize() { super.initialize(); - client.getIGame().addGameListener(gameListener); + client.getGame().addGameListener(gameListener); frame.setVisible(true); } @@ -128,7 +155,7 @@ protected boolean saveGame() { @Override public void die() { super.die(); - client.getIGame().removeGameListener(gameListener); + client.getGame().removeGameListener(gameListener); } @Override @@ -166,25 +193,6 @@ protected void switchPanel(GamePhase phase) { // cl.setBottom(cb.getComponent()); // getBoardView().getTilesetManager().reset(); break; - case POINTBLANK_SHOT: - case SET_ARTILLERY_AUTOHIT_HEXES: - case DEPLOY_MINEFIELDS: - case DEPLOYMENT: - case TARGETING: - case PREMOVEMENT: - case MOVEMENT: - case OFFBOARD: - case PREFIRING: - case FIRING: - case PHYSICAL: - case INITIATIVE_REPORT: - case TARGETING_REPORT: - case MOVEMENT_REPORT: - case OFFBOARD_REPORT: - case FIRING_REPORT: - case PHYSICAL_REPORT: - case END_REPORT: - case VICTORY: default: break; } @@ -225,72 +233,59 @@ protected void switchPanel(GamePhase phase) { // } } + private void initializeSingleComponent(GamePhase phase, JComponent component, String identifier) { + component.setName(identifier); + panMain.add(component, identifier); + String phaseName = String.valueOf(phase); + phaseComponents.put(phaseName, component); + mainNames.put(phaseName, identifier); + } + + private void initializeWithBoardView(GamePhase phase, StatusBarPhaseDisplay component, String secondary) { + String identifier = CG_BOARDVIEW; + String phaseName = String.valueOf(phase); + component.setName(identifier); + panMain.add(component, identifier); + if (!mainNames.containsValue(identifier)) { + panMain.add(panTop, identifier); + } + currPhaseDisplay = component; + panSecondary.add(component, secondary); + phaseComponents.put(phaseName, component); + mainNames.put(phaseName, identifier); + if (secondary != null) { + secondaryNames.put(phaseName, secondary); + } + } + private JComponent initializePanel(GamePhase phase) { // Create the components for this phase. - String name = String.valueOf(phase); JComponent component = new ReceivingGameDataPanel(); - String secondary = null; - String main; + String secondary; + String main = CG_BOARDVIEW; switch (phase) { case LOUNGE: -// component = new ChatLounge(this); -// chatlounge = (ChatLounge) component; - main = CG_CHATLOUNGE; - component.setName(main); - panMain.add(component, main); +// initializeSingleComponent(phase, new ChatLounge(this), CG_CHATLOUNGE); break; case STARTING_SCENARIO: - component = new JLabel(Messages.getString("ClientGUI.StartingScenario")); - UIUtil.scaleComp(component, UIUtil.FONT_SCALE1); - main = CG_STARTINGSCENARIO; - component.setName(main); - panMain.add(component, main); + initializeSingleComponent(phase, new StartingScenarioPanel(), CG_STARTINGSCENARIO); break; case EXCHANGE: -// chatlounge.killPreviewBV(); - component = new ReceivingGameDataPanel(); - UIUtil.scaleComp(component, UIUtil.FONT_SCALE1); - main = CG_EXCHANGE; - component.setName(main); - panMain.add(component, main); + initializeSingleComponent(phase, new ReceivingGameDataPanel(), CG_EXCHANGE); break; case SET_ARTILLERY_AUTOHIT_HEXES: -// component = new SelectArtyAutoHitHexDisplay(this); - main = CG_BOARDVIEW; - secondary = CG_SELECTARTYAUTOHITHEXDISPLAY; - component.setName(secondary); - if (!mainNames.containsValue(main)) { - panMain.add(panTop, main); - } - currPhaseDisplay = (StatusBarPhaseDisplay) component; - panSecondary.add(component, secondary); +// initializeWithBoardView(phase, new SelectArtyAutoHitHexDisplay(this), CG_SELECTARTYAUTOHITHEXDISPLAY); break; case DEPLOY_MINEFIELDS: -// component = new DeployMinefieldDisplay(this); - main = CG_BOARDVIEW; - secondary = CG_DEPLOYMINEFIELDDISPLAY; - component.setName(secondary); - if (!mainNames.containsValue(main)) { - panMain.add(panTop, main); - } - currPhaseDisplay = (StatusBarPhaseDisplay) component; - panSecondary.add(component, secondary); +// initializeWithBoardView(phase, new DeployMinefieldDisplay(this), CG_DEPLOYMINEFIELDDISPLAY); break; case DEPLOYMENT: -// component = new DeploymentDisplay(this); - main = CG_BOARDVIEW; - secondary = CG_DEPLOYMENTDISPLAY; - component.setName(secondary); - if (!mainNames.containsValue(main)) { - panMain.add(panTop, main); - } - currPhaseDisplay = (StatusBarPhaseDisplay) component; - panSecondary.add(component, secondary); +// initializeWithBoardView(phase, new DeploymentDisplay(this), CG_DEPLOYMINEFIELDDISPLAY); break; case TARGETING: +// initializeWithBoardView(phase, new TargetingPhaseDisplay(this, false), CG_DEPLOYMINEFIELDDISPLAY); // component = new TargetingPhaseDisplay(this, false); // ((TargetingPhaseDisplay) component).initializeListeners(); - main = CG_BOARDVIEW; secondary = CG_TARGETINGPHASEDISPLAY; component.setName(secondary); if (!mainNames.containsValue(main)) { @@ -303,7 +298,6 @@ private JComponent initializePanel(GamePhase phase) { case PREMOVEMENT: // component = new PrephaseDisplay(this, GamePhase.PREMOVEMENT); // ((PrephaseDisplay) component).initializeListeners(); - main = CG_BOARDVIEW; secondary = CG_PREMOVEMENTDISPLAY; component.setName(secondary); if (!mainNames.containsValue(main)) { @@ -313,20 +307,11 @@ private JComponent initializePanel(GamePhase phase) { panSecondary.add(component, secondary); break; case MOVEMENT: -// component = new MovementDisplay(this); - main = CG_BOARDVIEW; - secondary = CG_MOVEMENTDISPLAY; - component.setName(secondary); - if (!mainNames.containsValue(main)) { - panMain.add(panTop, main); - } - currPhaseDisplay = (StatusBarPhaseDisplay) component; - panSecondary.add(component, secondary); +// initializeWithBoardView(phase, new MovementDisplay(this), CG_MOVEMENTDISPLAY); break; case OFFBOARD: // component = new TargetingPhaseDisplay(this, true); // ((TargetingPhaseDisplay) component).initializeListeners(); - main = CG_BOARDVIEW; secondary = CG_OFFBOARDDISPLAY; component.setName(secondary); if (!mainNames.containsValue(main)) { @@ -338,7 +323,6 @@ private JComponent initializePanel(GamePhase phase) { case PREFIRING: // component = new PrephaseDisplay(this, GamePhase.PREFIRING); // ((PrephaseDisplay) component).initializeListeners(); - main = CG_BOARDVIEW; secondary = CG_PREFIRING; component.setName(secondary); if (!mainNames.containsValue(main)) { @@ -348,37 +332,13 @@ private JComponent initializePanel(GamePhase phase) { panSecondary.add(component, secondary); break; case FIRING: -// component = new FiringDisplay(this); - main = CG_BOARDVIEW; - secondary = CG_FIRINGDISPLAY; - component.setName(secondary); - if (!mainNames.containsValue(main)) { - panMain.add(panTop, main); - } - currPhaseDisplay = (StatusBarPhaseDisplay) component; - panSecondary.add(component, secondary); +// initializeWithBoardView(phase, new FiringDisplay(this), CG_FIRINGDISPLAY); break; case POINTBLANK_SHOT: -// component = new PointblankShotDisplay(this); - main = CG_BOARDVIEW; - secondary = CG_POINTBLANKSHOTDISPLAY; - component.setName(secondary); - if (!mainNames.containsValue(main)) { - panMain.add(panTop, main); - } - currPhaseDisplay = (StatusBarPhaseDisplay) component; - panSecondary.add(component, secondary); +// initializeWithBoardView(phase, new PointblankShotDisplay(this), CG_POINTBLANKSHOTDISPLAY); break; case PHYSICAL: -// component = new PhysicalDisplay(this); - main = CG_BOARDVIEW; - secondary = CG_PHYSICALDISPLAY; - component.setName(secondary); - if (!mainNames.containsValue(main)) { - panMain.add(panTop, main); - } - currPhaseDisplay = (StatusBarPhaseDisplay) component; - panSecondary.add(component, secondary); +// initializeWithBoardView(phase, new PhysicalDisplay(this), CG_PHYSICALDISPLAY); break; case INITIATIVE_REPORT: case TARGETING_REPORT: @@ -388,9 +348,10 @@ private JComponent initializePanel(GamePhase phase) { case PHYSICAL_REPORT: case END_REPORT: case VICTORY: - main = CG_BOARDVIEW; +// initializeWithBoardView(phase, new JPanel(), CG_PHYSICALDISPLAY); secondary = CG_REPORTDISPLAY; if (reportDisply == null) { +// reportDisply = new JPanel(); // reportDisply = new ReportDisplay(this); // reportDisply.setName(secondary); } @@ -399,24 +360,17 @@ private JComponent initializePanel(GamePhase phase) { } currPhaseDisplay = reportDisply; component = reportDisply; - if (!secondaryNames.containsValue(secondary)) { - panSecondary.add(reportDisply, secondary); - } +// if (!secondaryNames.containsValue(secondary)) { +// panSecondary.add(reportDisply, secondary); +// } break; default: component = new WaitingForServerPanel(); main = CG_DEFAULT; - secondary = main; component.setName(main); panMain.add(main, component); break; } - phaseComponents.put(name, component); - mainNames.put(name, main); - if (secondary != null) { - secondaryNames.put(name, secondary); - } - return component; } diff --git a/megamek/src/megamek/client/ui/swing/StartingScenarioPanel.java b/megamek/src/megamek/client/ui/swing/StartingScenarioPanel.java new file mode 100644 index 00000000000..dc557d460fb --- /dev/null +++ b/megamek/src/megamek/client/ui/swing/StartingScenarioPanel.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MegaMek. + * + * MegaMek is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MegaMek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MegaMek. If not, see . + */ + +package megamek.client.ui.swing; + +import megamek.client.ui.swing.util.FontHandler; +import megamek.client.ui.swing.util.UIUtil; + +import javax.swing.*; +import java.awt.*; + +public class StartingScenarioPanel extends JPanel { + + private static final String text = "Starting Scenario"; + private static final String sign = "\uf50e"; + + /** + * Returns a panel that shows the centered notice saying "Under Construction" with a warning sign above. + */ + public StartingScenarioPanel() { + JPanel textPanel = new UIUtil.FixedYPanel(new FlowLayout(FlowLayout.CENTER)); + textPanel.add(new JLabel(text)); + + JPanel symbolPanel = new UIUtil.FixedYPanel(new FlowLayout(FlowLayout.CENTER)); + JLabel symbolLabel = new JLabel(sign); + symbolLabel.setFont(FontHandler.symbolFont()); + symbolPanel.add(symbolLabel); + + setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + add(Box.createVerticalGlue()); + add(symbolPanel); + add(textPanel); + add(Box.createVerticalGlue()); + } +} diff --git a/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java b/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java index 8f79802398d..1cb421424e2 100644 --- a/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java +++ b/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java @@ -54,6 +54,7 @@ public abstract class StatusBarPhaseDisplay extends AbstractPhaseDisplay protected static final GUIPreferences GUIP = GUIPreferences.getInstance(); private static final int BUTTON_ROWS = 2; private static final String SBPD_KEY_CLEARBUTTON = "clearButton"; + protected ClientGUI clientgui; /** * timer that ends turn if time limit set in options is over @@ -101,6 +102,7 @@ public int compare(PhaseCommand c1, PhaseCommand c2) { protected StatusBarPhaseDisplay(ClientGUI cg) { super(cg); + this.clientgui = cg; getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), SBPD_KEY_CLEARBUTTON); getActionMap().put(SBPD_KEY_CLEARBUTTON, new AbstractAction() { @Override diff --git a/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java b/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java index e5fdc2af820..645eaf710c0 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java +++ b/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java @@ -268,11 +268,13 @@ public class ChatLounge extends AbstractPhaseDisplay implements private static final GUIPreferences GUIP = GUIPreferences.getInstance(); + protected ClientGUI clientgui; + /** Creates a new chat lounge for the clientgui.getClient(). */ public ChatLounge(ClientGUI clientgui) { super(clientgui, SkinSpecification.UIComponents.ChatLounge.getComp(), SkinSpecification.UIComponents.ChatLoungeDoneButton.getComp()); - + this.clientgui = clientgui; setLayout(new BorderLayout()); splitPaneMain = new JSplitPane(JSplitPane.VERTICAL_SPLIT); splitPaneMain.setDividerSize(15); @@ -3607,5 +3609,10 @@ public void killPreviewBV() { previewBV.dispose(); } } + + @Override + public ClientGUI getClientgui() { + return clientgui; + } } diff --git a/megamek/src/megamek/client/ui/swing/util/TurnTimer.java b/megamek/src/megamek/client/ui/swing/util/TurnTimer.java index af7e0ad929d..5228ec71bef 100644 --- a/megamek/src/megamek/client/ui/swing/util/TurnTimer.java +++ b/megamek/src/megamek/client/ui/swing/util/TurnTimer.java @@ -60,7 +60,7 @@ public TurnTimer(int limit, AbstractPhaseDisplay pD, Client client) { int seconds = timeLimit % 60; int minutes = timeLimit / 60; remaining = new JLabel(String.format("%s:%02d", minutes, seconds)); - phaseDisplay.getClientgui().getMenuBar().add(display); + phaseDisplay.getClientgui().turnTimerComponent().add(display); display.setLayout(new FlowLayout()); display.add(remaining); display.add(progressBar); @@ -97,7 +97,7 @@ public void actionPerformed(ActionEvent ae) { phaseDisplay.ready(); timer.stop(); display.setVisible(false); - phaseDisplay.getClientgui().getMenuBar().remove(display); + phaseDisplay.getClientgui().turnTimerComponent().remove(display); } } }; @@ -106,7 +106,7 @@ public void actionPerformed(ActionEvent ae) { public void startTimer() { SwingUtilities.invokeLater(() -> { timer = new Timer(1000, listener); - phaseDisplay.getClientgui().getMenuBar().add(display); + phaseDisplay.getClientgui().turnTimerComponent().add(display); timer.start(); display.setVisible(true); @@ -116,8 +116,8 @@ public void startTimer() { public void stopTimer() { display.setVisible(false); - if (phaseDisplay.getClientgui().getMenuBar() != null) { - phaseDisplay.getClientgui().getMenuBar().remove(display); + if (phaseDisplay.getClientgui().turnTimerComponent() != null) { + phaseDisplay.getClientgui().turnTimerComponent().remove(display); } if (timer != null) { diff --git a/megamek/src/megamek/common/Game.java b/megamek/src/megamek/common/Game.java index d513c0af92a..e08c6714e32 100644 --- a/megamek/src/megamek/common/Game.java +++ b/megamek/src/megamek/common/Game.java @@ -734,11 +734,6 @@ public GamePhase getPhase() { return phase; } - @Override - public void receivePhase(GamePhase phase) { - setPhase(phase); - } - @Override public void setPhase(GamePhase phase) { final GamePhase oldPhase = this.phase; diff --git a/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java b/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java index ac2baa31999..431e1dc28ee 100644 --- a/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java +++ b/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java @@ -23,6 +23,7 @@ import megamek.common.annotations.Nullable; import megamek.common.enums.GamePhase; import megamek.common.event.GameListener; +import megamek.common.event.GamePhaseChangeEvent; import megamek.common.options.GameOptions; import megamek.common.options.OptionsConstants; import megamek.common.planetaryconditions.PlanetaryConditions; @@ -69,6 +70,13 @@ public void setPhase(GamePhase phase) { this.phase = phase; } + @Override + public void receivePhase(GamePhase phase) { + GamePhase oldPhase = this.phase; + setPhase(phase); + fireGameEvent(new GamePhaseChangeEvent(this, oldPhase, phase)); + } + @Override public boolean isForceVictory() { //TODO This should not be part of IGame! too specific return false; From 6c9d670ee3d6c6aafb20c70c95672d3d84870127 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 9 May 2024 22:54:19 +0200 Subject: [PATCH 06/11] various cleanup --- .../client/ui/swing/AbstractPhaseDisplay.java | 46 ++++++++----------- .../megamek/client/ui/swing/ClientGUI.java | 22 ++++----- .../megamek/client/ui/swing/SBFClientGUI.java | 10 ++-- 3 files changed, 33 insertions(+), 45 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java b/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java index 654b0492ae5..d7f922ae938 100644 --- a/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java +++ b/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java @@ -22,14 +22,12 @@ import megamek.common.util.Distractable; import megamek.common.util.DistractableAdapter; -import javax.swing.*; -import java.awt.event.ActionEvent; +import java.util.Objects; import static megamek.client.ui.swing.util.UIUtil.guiScaledFontHTML; public abstract class AbstractPhaseDisplay extends SkinnedJPanel implements BoardViewListener, GameListener, Distractable { - private static final long serialVersionUID = 4421205210788230341L; public static final int DONE_BUTTON_WIDTH = 160; @@ -44,34 +42,26 @@ protected AbstractPhaseDisplay(IClientGUI cg) { protected AbstractPhaseDisplay(IClientGUI cg, String borderSkinComp, String buttonSkinComp) { super(borderSkinComp, 0); - clientgui = cg; + clientgui = Objects.requireNonNull(cg); setBorder(new MegamekBorder(borderSkinComp)); butDone = new MegamekButton("DONE", buttonSkinComp); String f = guiScaledFontHTML(UIUtil.uiLightViolet()) + KeyCommandBind.getDesc(KeyCommandBind.DONE)+ ""; butDone.setToolTipText("" + f + ""); butDone.setActionCommand("doneButton"); - if (clientgui != null) { - butDone.addActionListener(new AbstractAction() { - private static final long serialVersionUID = -5034474968902280850L; - - @Override - public void actionPerformed(ActionEvent e) { - if (isIgnoringEvents()) { - return; - } - if ((clientgui.getClient().isMyTurn()) - || (clientgui.getClient().getGame().getTurn() == null) - || (clientgui.getClient().getGame().getPhase().isReport())) { - ready(); - // When the turn is ended, we could miss a key release event - // This will ensure no repeating keys are stuck down - MegaMekGUI.getKeyDispatcher().stopAllRepeating(); - } - } - }); - - MegaMekGUI.getKeyDispatcher().registerCommandAction(KeyCommandBind.DONE, this::shouldPerformKeyCommands, this::ready); - } + butDone.addActionListener(e -> { + if (shouldPerformKeyCommands()) { + done(); + } + }); + + MegaMekGUI.getKeyDispatcher().registerCommandAction(KeyCommandBind.DONE, this::shouldPerformKeyCommands, this::done); + } + + private void done() { + // When the turn is ended, we could miss a key release event + // This will ensure no repeating keys are stuck down + MegaMekGUI.getKeyDispatcher().stopAllRepeating(); + ready(); } private boolean shouldPerformKeyCommands() { @@ -85,12 +75,12 @@ && isVisible() } @Override - public boolean isIgnoringEvents() { + public final boolean isIgnoringEvents() { return distracted.isIgnoringEvents(); } @Override - public void setIgnoringEvents(boolean distracted) { + public final void setIgnoringEvents(boolean distracted) { this.distracted.setIgnoringEvents(distracted); } diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java index 86db4d96974..2cec963126f 100644 --- a/megamek/src/megamek/client/ui/swing/ClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java @@ -300,7 +300,7 @@ public class ClientGUI extends AbstractClientGUI implements BoardViewListener, */ private final JPanel panSecondary = new JPanel(); - private ReportDisplay reportDisply; + private ReportDisplay reportDisplay; private StatusBarPhaseDisplay currPhaseDisplay; @@ -672,16 +672,16 @@ public void miniReportDisplayAddReportPages() { } public void reportDisplayResetDone() { - if ((reportDisply != null) && (!getClient().getLocalPlayer().isDone())) { - reportDisply.setDoneEnabled(true); + if ((reportDisplay != null) && (!getClient().getLocalPlayer().isDone())) { + reportDisplay.setDoneEnabled(true); } } public void reportDisplayResetRerollInitiative() { - if ((reportDisply != null) + if ((reportDisplay != null) && (!getClient().getLocalPlayer().isDone()) && (getClient().getGame().hasTacticalGenius(getClient().getLocalPlayer()))) { - reportDisply.resetRerollInitiativeEnabled(); + reportDisplay.resetRerollInitiativeEnabled(); } } @@ -1349,17 +1349,17 @@ private JComponent initializePanel(GamePhase phase) { case VICTORY: main = CG_BOARDVIEW; secondary = CG_REPORTDISPLAY; - if (reportDisply == null) { - reportDisply = new ReportDisplay(this); - reportDisply.setName(secondary); + if (reportDisplay == null) { + reportDisplay = new ReportDisplay(this); + reportDisplay.setName(secondary); } if (!mainNames.containsValue(main)) { panMain.add(panTop, main); } - currPhaseDisplay = reportDisply; - component = reportDisply; + currPhaseDisplay = reportDisplay; + component = reportDisplay; if (!secondaryNames.containsValue(secondary)) { - panSecondary.add(reportDisply, secondary); + panSecondary.add(reportDisplay, secondary); } break; default: diff --git a/megamek/src/megamek/client/ui/swing/SBFClientGUI.java b/megamek/src/megamek/client/ui/swing/SBFClientGUI.java index 9961ea3e45e..6075c80d017 100644 --- a/megamek/src/megamek/client/ui/swing/SBFClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/SBFClientGUI.java @@ -19,7 +19,6 @@ package megamek.client.ui.swing; import megamek.client.SBFClient; -import megamek.client.ui.Messages; import megamek.client.ui.swing.util.MegaMekController; import megamek.common.InGameObject; import megamek.common.enums.GamePhase; @@ -81,7 +80,7 @@ public class SBFClientGUI extends AbstractClientGUI implements ActionListener { private final JPanel panSecondary = new JPanel(); - private ReportDisplay reportDisply; + private ReportDisplay reportDisplay; private StatusBarPhaseDisplay currPhaseDisplay; @@ -108,7 +107,6 @@ public SBFClientGUI(SBFClient client, MegaMekController c) { this.client = client; frame.getContentPane().setLayout(new BorderLayout()); frame.getContentPane().add(clientGuiPanel, BorderLayout.CENTER); -// frame.getContentPane().add(new UnderConstructionPanel(), BorderLayout.CENTER); frame.setJMenuBar(menuBar); menuBar.addActionListener(this); panMain.setLayout(cardsMain); @@ -350,7 +348,7 @@ private JComponent initializePanel(GamePhase phase) { case VICTORY: // initializeWithBoardView(phase, new JPanel(), CG_PHYSICALDISPLAY); secondary = CG_REPORTDISPLAY; - if (reportDisply == null) { + if (reportDisplay == null) { // reportDisply = new JPanel(); // reportDisply = new ReportDisplay(this); // reportDisply.setName(secondary); @@ -358,8 +356,8 @@ private JComponent initializePanel(GamePhase phase) { if (!mainNames.containsValue(main)) { panMain.add(panTop, main); } - currPhaseDisplay = reportDisply; - component = reportDisply; + currPhaseDisplay = reportDisplay; + component = reportDisplay; // if (!secondaryNames.containsValue(secondary)) { // panSecondary.add(reportDisply, secondary); // } From 99b0bff2c9306c62e7609f9d97161fba388c4808 Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 12 May 2024 10:42:43 +0200 Subject: [PATCH 07/11] remove unnecessary code from WeaponPanel, comments --- .../megamek/client/ui/swing/ClientGUI.java | 38 +- .../client/ui/swing/DeploymentDisplay.java | 10 +- .../client/ui/swing/FiringDisplay.java | 4 +- .../client/ui/swing/MovementDisplay.java | 32 +- .../ui/swing/PointblankShotDisplay.java | 4 +- .../client/ui/swing/PrephaseDisplay.java | 4 +- .../ui/swing/StatusBarPhaseDisplay.java | 22 - .../ui/swing/TargetingPhaseDisplay.java | 4 +- .../client/ui/swing/boardview/BoardView.java | 193 --------- .../boardview/FiringArcSpriteHandler.java | 408 ++++++++++++++++++ .../ui/swing/unitDisplay/UnitDisplay.java | 5 +- .../ui/swing/unitDisplay/WeaponPanel.java | 168 +------- megamek/src/megamek/common/Mounted.java | 34 +- 13 files changed, 485 insertions(+), 441 deletions(-) create mode 100644 megamek/src/megamek/client/ui/swing/boardview/FiringArcSpriteHandler.java diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java index 2cec963126f..d61b7f59b48 100644 --- a/megamek/src/megamek/client/ui/swing/ClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java @@ -57,6 +57,7 @@ import megamek.common.actions.WeaponAttackAction; import megamek.common.annotations.Nullable; import megamek.common.enums.GamePhase; +import megamek.common.equipment.WeaponMounted; import megamek.common.event.*; import megamek.common.icons.Camouflage; import megamek.common.preference.IPreferenceChangeListener; @@ -234,6 +235,7 @@ public class ClientGUI extends AbstractClientGUI implements BoardViewListener, private SensorRangeSpriteHandler sensorRangeSpriteHandler; private CollapseWarningSpriteHandler collapseWarningSpriteHandler; private FiringSolutionSpriteHandler firingSolutionSpriteHandler; + private FiringArcSpriteHandler firingArcSpriteHandler; private final List spriteHandlers = new ArrayList<>(); private JPanel panTop; private JSplitPane splitPaneA; @@ -470,10 +472,11 @@ private void initializeSpriteHandlers() { sensorRangeSpriteHandler = new SensorRangeSpriteHandler(bv, client.getGame()); collapseWarningSpriteHandler = new CollapseWarningSpriteHandler(bv); firingSolutionSpriteHandler = new FiringSolutionSpriteHandler(bv, client); + firingArcSpriteHandler = new FiringArcSpriteHandler(bv, this); spriteHandlers.addAll(List.of(movementEnvelopeHandler, movementModifierSpriteHandler, sensorRangeSpriteHandler, flareSpritesHandler, collapseWarningSpriteHandler, - firingSolutionSpriteHandler)); + firingSolutionSpriteHandler, firingArcSpriteHandler)); spriteHandlers.forEach(BoardViewSpriteHandler::initialize); } @@ -2216,7 +2219,7 @@ public void gameReport(GameReportEvent e) { @Override public void gameEnd(GameEndEvent e) { bv.clearMovementData(); - bv.clearFieldOfFire(); + clearFieldOfFire(); clearTemporarySprites(); getLocalBots().values().forEach(AbstractClient::die); getLocalBots().clear(); @@ -2841,6 +2844,7 @@ public void clearTemporarySprites() { sensorRangeSpriteHandler.clear(); collapseWarningSpriteHandler.clear(); firingSolutionSpriteHandler.clear(); + firingArcSpriteHandler.clear(); } /** @@ -2894,6 +2898,7 @@ public void showFiringSolutions(Entity entity) { public void weaponSelected(MechDisplayEvent b) { setSelectedEntityNum(b.getEntityId()); setSelectedWeapon(b.getWeaponId()); + firingArcSpriteHandler.updateSelectedWeapon(b.getEntity(), getSelectedWeapon()); } private void setSelectedWeapon(int equipmentNumber) { @@ -2909,8 +2914,8 @@ public int getSelectedWeaponId() { } @Nullable - public Mounted getSelectedWeapon() { - return hasSelectedWeapon() ? getSelectedUnit().getEquipment(selectedWeapon) : null; + public WeaponMounted getSelectedWeapon() { + return hasSelectedWeapon() ? (WeaponMounted) getSelectedUnit().getEquipment(selectedWeapon) : null; } @Nullable @@ -2926,4 +2931,29 @@ public boolean hasSelectedWeapon() { public JPanel getMainPanel() { return clientGuiPanel; } + + public void setFiringArcPosition(Entity entity, MovePath movePath) { + // Only update when the selected weapon is on the unit that's moving + if (entity.getId() == getSelectedEntityNum()) { + firingArcSpriteHandler.updatePosition(movePath); + } + } + + public void setFiringArcPosition(Entity entity, Coords coords) { + // Only update when the selected weapon is on the unit that's changing position + if (entity.getId() == getSelectedEntityNum()) { + firingArcSpriteHandler.updatePosition(coords); + } + } + + public void setFiringArcFacing(Entity entity) { + // Only update when the selected weapon is on the unit that's changing its facing + if (entity.getId() == getSelectedEntityNum()) { + firingArcSpriteHandler.updateFacing(entity.getFacing()); + } + } + + public void clearFieldOfFire() { + firingArcSpriteHandler.clearValues(); + } } diff --git a/megamek/src/megamek/client/ui/swing/DeploymentDisplay.java b/megamek/src/megamek/client/ui/swing/DeploymentDisplay.java index 53274612f28..2b8b8e4e1c3 100644 --- a/megamek/src/megamek/client/ui/swing/DeploymentDisplay.java +++ b/megamek/src/megamek/client/ui/swing/DeploymentDisplay.java @@ -226,13 +226,13 @@ public void selectEntity(int en) { clientgui.getUnitDisplay().displayEntity(ce()); clientgui.getUnitDisplay().showPanel("movement"); - clientgui.getBoardView().setWeaponFieldOfFire(ce().getFacing(), ce().getPosition()); + clientgui.setFiringArcPosition(ce(), ce().getPosition()); clientgui.showSensorRanges(ce()); computeCFWarningHexes(ce()); } else { disableButtons(); setNextEnabled(true); - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); } } @@ -515,7 +515,7 @@ public void hexMoused(BoardViewEvent b) { ce().setFacing(ce().getPosition().direction(moveto)); ce().setSecondaryFacing(ce().getFacing()); clientgui.getBoardView().redrawEntity(ce()); - clientgui.getBoardView().setWeaponFieldOfFire(ce().getFacing(), ce().getPosition()); + clientgui.setFiringArcFacing(ce()); clientgui.showSensorRanges(ce()); turnMode = false; } else if (ce().isBoardProhibited(board.getType())) { @@ -581,7 +581,7 @@ public void hexMoused(BoardViewEvent b) { ce().setPosition(moveto); clientgui.getBoardView().redrawEntity(ce()); - clientgui.getBoardView().setWeaponFieldOfFire(ce().getFacing(), moveto); + clientgui.setFiringArcPosition(ce(), moveto); clientgui.showSensorRanges(ce()); clientgui.getBoardView().getPanel().repaint(); butDone.setEnabled(true); @@ -871,7 +871,7 @@ public void unitSelected(BoardViewEvent b) { if (null == e) { return; } - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); if (client.isMyTurn()) { if (client.getGame().getTurn().isValidEntity(e, client.getGame())) { diff --git a/megamek/src/megamek/client/ui/swing/FiringDisplay.java b/megamek/src/megamek/client/ui/swing/FiringDisplay.java index ca485a771a5..26cd70fd97d 100644 --- a/megamek/src/megamek/client/ui/swing/FiringDisplay.java +++ b/megamek/src/megamek/client/ui/swing/FiringDisplay.java @@ -469,7 +469,7 @@ protected void beginMyTurn() { if (!clientgui.getBoardView().isMovingUnits()) { clientgui.maybeShowUnitDisplay(); } - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); selectEntity(clientgui.getClient().getFirstEntityNum()); @@ -532,7 +532,7 @@ protected void endMyTurn() { clientgui.getBoardView().cursor(null); clientgui.getBoardView().clearMovementData(); clientgui.getBoardView().clearStrafingCoords(); - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); clientgui.setSelectedEntityNum(Entity.NONE); disableButtons(); diff --git a/megamek/src/megamek/client/ui/swing/MovementDisplay.java b/megamek/src/megamek/client/ui/swing/MovementDisplay.java index 461bfc08299..4da78042b92 100644 --- a/megamek/src/megamek/client/ui/swing/MovementDisplay.java +++ b/megamek/src/megamek/client/ui/swing/MovementDisplay.java @@ -442,7 +442,11 @@ private void computeEnvelope() { private void cancel() { clear(); - computeMovementEnvelope(ce()); + Entity currentEntity = ce(); + if (currentEntity != null) { + computeMovementEnvelope(currentEntity); + clientgui.setFiringArcPosition(currentEntity, currentEntity.getPosition()); + } updateMove(); } @@ -669,7 +673,7 @@ public synchronized void selectEntity(int en) { } else { setStatusBarText(yourTurnMsg); } - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); computeMovementEnvelope(ce); updateMove(); computeCFWarningHexes(ce); @@ -1071,7 +1075,7 @@ private void beginMyTurn() { initDonePanelForNewTurn(); setNextEnabled(true); setForwardIniEnabled(true); - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); if (numButtonGroups > 1) { getBtn(MoveCommand.MOVE_MORE).setEnabled(true); @@ -1112,7 +1116,7 @@ private synchronized void endMyTurn() { clientgui.getBoardView().selectEntity(null); clientgui.setSelectedEntityNum(Entity.NONE); clientgui.getBoardView().clearMovementData(); - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); } @@ -1200,7 +1204,7 @@ public void clear() { // clear board cursors clientgui.getBoardView().select(null); clientgui.getBoardView().cursor(null); - clientgui.clearTemporarySprites(); +// clientgui.clearTemporarySprites(); if (ce == null) { return; @@ -1221,7 +1225,7 @@ public void clear() { // create new current and considered paths cmd = new MovePath(clientgui.getClient().getGame(), ce); - clientgui.getBoardView().setWeaponFieldOfFire(ce, cmd); + clientgui.setFiringArcPosition(ce, cmd); clientgui.showSensorRanges(ce, cmd.getFinalCoords()); computeCFWarningHexes(ce); @@ -1309,7 +1313,7 @@ private void removeLastStep() { clientgui.getBoardView().select(cmd.getFinalCoords()); clientgui.getBoardView().cursor(cmd.getFinalCoords()); clientgui.getBoardView().drawMovementData(entity, cmd); - clientgui.getBoardView().setWeaponFieldOfFire(entity, cmd); + clientgui.setFiringArcPosition(entity, cmd); clientgui.showSensorRanges(entity, cmd.getFinalCoords()); //FIXME what is this @@ -1822,7 +1826,7 @@ private void currentMove(Coords dest) { } clientgui.showSensorRanges(ce(), cmd.getFinalCoords()); - clientgui.getBoardView().setWeaponFieldOfFire(ce(), cmd); + clientgui.setFiringArcPosition(ce(), cmd); } // @@ -4352,7 +4356,6 @@ public void gamePhaseChange(GamePhaseChangeEvent e) { public void computeMovementEnvelope(Entity suggestion) { // do nothing if deactivated in the settings if (!GUIP.getMoveEnvelope()) { - clientgui.clearTemporarySprites(); return; } @@ -5708,17 +5711,6 @@ private void updateTurnButton() { && !(cmd.isJumping() && (ce instanceof Mech) && (ce.getJumpType() == Mech.JUMP_BOOSTER))); } - @Override - public void setWeaponFieldOfFire(Entity unit, int[][] ranges, int arc, int loc) { - // If the unit is the current unit, then work with - // the current considered movement - if (unit.equals(ce())) { - super.setWeaponFieldOfFire(unit, ranges, arc, loc, cmd); - } else { - super.setWeaponFieldOfFire(unit, ranges, arc, loc); - } - } - /** Shortcut to clientgui.getClient().getGame(). */ private Game game() { return clientgui.getClient().getGame(); diff --git a/megamek/src/megamek/client/ui/swing/PointblankShotDisplay.java b/megamek/src/megamek/client/ui/swing/PointblankShotDisplay.java index d07ddd38715..7776095abb9 100644 --- a/megamek/src/megamek/client/ui/swing/PointblankShotDisplay.java +++ b/megamek/src/megamek/client/ui/swing/PointblankShotDisplay.java @@ -310,7 +310,7 @@ public void selectEntity(int en) { @Override public void beginMyTurn() { clientgui.maybeShowUnitDisplay(); - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); butDone.setEnabled(true); @@ -341,7 +341,7 @@ protected void endMyTurn() { clientgui.getBoardView().cursor(null); clientgui.getBoardView().clearMovementData(); clientgui.getBoardView().clearStrafingCoords(); - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); clientgui.setSelectedEntityNum(Entity.NONE); disableButtons(); diff --git a/megamek/src/megamek/client/ui/swing/PrephaseDisplay.java b/megamek/src/megamek/client/ui/swing/PrephaseDisplay.java index f8eb6f3bb57..0989db53cb4 100644 --- a/megamek/src/megamek/client/ui/swing/PrephaseDisplay.java +++ b/megamek/src/megamek/client/ui/swing/PrephaseDisplay.java @@ -288,7 +288,7 @@ private void beginMyTurn() { setStatusBarText(Messages.getFormattedString("PrephaseDisplay.its_your_turn", phase.toString(), "")); butDone.setText("" + Messages.getString("PrephaseDisplay.Done") + ""); - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); selectEntity(clientgui.getClient().getFirstEntityNum()); @@ -311,7 +311,7 @@ private void endMyTurn() { clientgui.getBoardView().highlight(null); clientgui.getBoardView().cursor(null); clientgui.getBoardView().clearMovementData(); - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); clientgui.setSelectedEntityNum(Entity.NONE); diff --git a/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java b/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java index 1cb421424e2..3e46b6f9dae 100644 --- a/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java +++ b/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java @@ -400,26 +400,4 @@ public void setStatusBarWithNotDonePlayers() { } } } - - public void setWeaponFieldOfFire(Entity unit, int[][] ranges, int arc, int loc) { - setWeaponFieldOfFire(unit, ranges, arc, loc, unit.getFacing()); - } - - public void setWeaponFieldOfFire(Entity unit, int[][] ranges, int arc, int loc, int facing) { - clientgui.getBoardView().fieldOfFireUnit = unit; - clientgui.getBoardView().fieldOfFireRanges = ranges; - clientgui.getBoardView().fieldOfFireWpArc = arc; - clientgui.getBoardView().fieldOfFireWpLoc = loc; - - clientgui.getBoardView().setWeaponFieldOfFire(facing, unit.getPosition()); - } - - public void setWeaponFieldOfFire(Entity unit, int[][] ranges, int arc, int loc, MovePath cmd) { - clientgui.getBoardView().fieldOfFireUnit = unit; - clientgui.getBoardView().fieldOfFireRanges = ranges; - clientgui.getBoardView().fieldOfFireWpArc = arc; - clientgui.getBoardView().fieldOfFireWpLoc = loc; - - clientgui.getBoardView().setWeaponFieldOfFire(unit, cmd); - } } \ No newline at end of file diff --git a/megamek/src/megamek/client/ui/swing/TargetingPhaseDisplay.java b/megamek/src/megamek/client/ui/swing/TargetingPhaseDisplay.java index 0d2990884db..56c81f45133 100644 --- a/megamek/src/megamek/client/ui/swing/TargetingPhaseDisplay.java +++ b/megamek/src/megamek/client/ui/swing/TargetingPhaseDisplay.java @@ -384,7 +384,7 @@ private void beginMyTurn() { if (!clientgui.getBoardView().isMovingUnits()) { clientgui.maybeShowUnitDisplay(); } - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); selectEntity(clientgui.getClient().getFirstEntityNum()); @@ -444,7 +444,7 @@ private void endMyTurn() { clientgui.getBoardView().highlight(null); clientgui.getBoardView().cursor(null); clientgui.getBoardView().clearMovementData(); - clientgui.getBoardView().clearFieldOfFire(); + clientgui.clearFieldOfFire(); clientgui.clearTemporarySprites(); clientgui.setSelectedEntityNum(Entity.NONE); disableButtons(); diff --git a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java index 177b247ad36..4e92df821c2 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java +++ b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java @@ -204,16 +204,6 @@ public final class BoardView extends AbstractBoardView implements BoardListener, // vector of sprites for aero flyover lines private ArrayList flyOverSprites = new ArrayList<>(); - // List of sprites for the weapon field of fire - private ArrayList fieldOfFireSprites = new ArrayList<>(); - public int[][] fieldOfFireRanges = {new int[5], new int[5]}; - public int fieldOfFireWpArc; - public Entity fieldOfFireUnit; - public int fieldOfFireWpLoc; - // int because it acts as an array index - public int fieldOfFireWpUnderwater = 0; - private static final String[] rangeTexts = {"min", "S", "M", "L", "E"}; - TilesetManager tileManager; // polygons for a few things @@ -871,11 +861,6 @@ public void draw(Graphics g) { drawSprites(g, wreckSprites); } - // Field of Fire - if (!useIsometric() && shouldShowFieldOfFire()) { - drawSprites(g, fieldOfFireSprites); - } - // Minefield signs all over the place! drawMinefields(g); @@ -1597,11 +1582,6 @@ public BufferedImage getEntireBoardImage(boolean ignoreUnits, boolean useBaseZoo drawSprites(boardGraph, wreckSprites); } - // Field of Fire - if (!useIsometric() && shouldShowFieldOfFire()) { - drawSprites(boardGraph, fieldOfFireSprites); - } - // Minefield signs all over the place! drawMinefields(boardGraph); @@ -1715,9 +1695,6 @@ private void drawHexes(Graphics g, Rectangle view, boolean saveBoardImage) { if ((hex != null)) { drawHex(c, g, saveBoardImage); drawOrthograph(c, g); - if (shouldShowFieldOfFire()) { - drawHexSpritesForHex(c, g, fieldOfFireSprites); - } drawHexSpritesForHex(c, g, behindTerrainHexSprites); if ((en_Deployer != null) && board.isLegalDeployment(c, en_Deployer)) { @@ -4305,7 +4282,6 @@ public void clearSprites() { vtolAttackSprites.clear(); flyOverSprites.clear(); movementSprites.clear(); - fieldOfFireSprites.clear(); overTerrainSprites.clear(); behindTerrainHexSprites.clear(); @@ -4755,10 +4731,6 @@ private void zoom() { allSprites.forEach(Sprite::prepare); - for (Sprite spr : fieldOfFireSprites) { - spr.prepare(); - } - updateFontSizes(); updateBoard(); @@ -4887,10 +4859,6 @@ public boolean toggleIsometric() { allSprites.forEach(Sprite::prepare); hexSprites.forEach(HexSprite::updateBounds); - for (Sprite spr : fieldOfFireSprites) { - spr.prepare(); - } - clearHexImageCache(); updateBoard(); boardPanel.repaint(); @@ -4979,167 +4947,6 @@ public Polygon getHexPoly() { return hexPoly; } - public void clearFieldOfFire() { - fieldOfFireWpArc = -1; - fieldOfFireUnit = null; - fieldOfFireSprites.clear(); - boardPanel.repaint(); - } - - // this is called from MovementDisplay and checks if - // the unit ends up underwater - public void setWeaponFieldOfFire(Entity ce, MovePath cmd) { - // if lack of data: clear and return - if ((fieldOfFireUnit == null) - || (ce == null) - || (cmd == null)) { - clearFieldOfFire(); - return; - } - - // If the field of fire is not displayed - // for the active unit, then don't change anything - if (fieldOfFireUnit.equals(ce)) { - - fieldOfFireWpUnderwater = 0; - // check if the weapon ends up underwater - Hex hex = game.getBoard().getHex(cmd.getFinalCoords()); - - if ((hex.terrainLevel(Terrains.WATER) > 0) && !cmd.isJumping() - && (cmd.getFinalElevation() < 0)) { - if ((fieldOfFireUnit instanceof Mech) && !fieldOfFireUnit.isProne() - && (hex.terrainLevel(Terrains.WATER) == 1)) { - if ((fieldOfFireWpLoc == Mech.LOC_RLEG) || (fieldOfFireWpLoc == Mech.LOC_LLEG)) { - fieldOfFireWpUnderwater = 1; - } - - if (fieldOfFireUnit instanceof QuadMech) { - if ((fieldOfFireWpLoc == Mech.LOC_RARM) - || (fieldOfFireWpLoc == Mech.LOC_LARM)) { - fieldOfFireWpUnderwater = 1; - } - } - if (fieldOfFireUnit instanceof TripodMech) { - if (fieldOfFireWpLoc == Mech.LOC_CLEG) { - fieldOfFireWpUnderwater = 1; - } - } - } else { - fieldOfFireWpUnderwater = 1; - } - } - setWeaponFieldOfFire(cmd.getFinalFacing(), cmd.getFinalCoords()); - } - } - - // prepares the sprites for a field of fire - public void setWeaponFieldOfFire(int fac, Coords c) { - if (fieldOfFireUnit == null || c == null) { - clearFieldOfFire(); - return; - } - - // Do not display anything for offboard units - if (fieldOfFireUnit.isOffBoard()) { - clearFieldOfFire(); - return; - } - - // check if extreme range is used - int maxrange = 4; - if (!game.getBoard().onGround() || game.getOptions().booleanOption(OptionsConstants.ADVCOMBAT_TACOPS_RANGE)) { - maxrange = 5; - } - - // create the lists of hexes - List> fieldFire = new ArrayList<>(5); - int range = 1; - // for all available range brackets Min/S/M/L/E ... - for (int bracket = 0; bracket < maxrange; bracket++) { - fieldFire.add(new HashSet<>()); - // Add all hexes up to the weapon range to separate lists - while (range <= fieldOfFireRanges[fieldOfFireWpUnderwater][bracket]) { - fieldFire.get(bracket).addAll(c.allAtDistance(range)); - range++; - if (range > 100) { - break; // only to avoid hangs - } - } - - // Remove hexes that are not on the board or not in the arc - fieldFire.get(bracket).removeIf(h -> !game.getBoard().contains(h) || !Compute.isInArc(c, fac, h, fieldOfFireWpArc)); - } - - // create the sprites - // - fieldOfFireSprites.clear(); - - // for all available range brackets Min/S/M/L/E ... - for (int bracket = 0; bracket < fieldFire.size(); bracket++) { - if (fieldFire.get(bracket) == null) { - continue; - } - for (Coords loc : fieldFire.get(bracket)) { - // check surrounding hexes - int edgesToPaint = 0; - for (int dir = 0; dir < 6; dir++) { - Coords adjacentHex = loc.translated(dir); - if (!fieldFire.get(bracket).contains(adjacentHex)) { - edgesToPaint += (1 << dir); - } - } - // create sprite if there's a border to paint - if (edgesToPaint > 0) { - FieldofFireSprite ffSprite = new FieldofFireSprite(this, bracket, loc, edgesToPaint); - fieldOfFireSprites.add(ffSprite); - } - } - // Add range markers (m, S, M, L, E) - // this looks for a hex in the middle of the range bracket; - // if outside the board, nearer hexes will be tried until - // the inner edge of the range bracket is reached - // the directions tested are those that fall between the - // hex facings because this makes for a better placement - // ... most of the time... - - // The directions[][] is used to make the marker placement - // fairly symmetrical to the unit facing which a simple for - // loop over the hex facings doesn't do - int[][] directions = {{0, 1}, {0, 5}, {3, 2}, {3, 4}, {1, 2}, {5, 4}}; - // don't paint too many "min" markers - int numMinMarkers = 0; - for (int[] dir : directions) { - // find the middle of the range bracket - int rangeend = Math.max(fieldOfFireRanges[fieldOfFireWpUnderwater][bracket], 0); - int rangebegin = 1; - if (bracket > 0) { - rangebegin = Math.max(fieldOfFireRanges[fieldOfFireWpUnderwater][bracket - 1] + 1, 1); - } - int dist = (rangeend + rangebegin) / 2; - // translate to the middle of the range bracket - Coords mark = c.translated((dir[0] + fac) % 6, (dist + 1) / 2) - .translated((dir[1] + fac) % 6, dist / 2); - // traverse back to the unit until a hex is onboard - while (!game.getBoard().contains(mark)) { - mark = Coords.nextHex(mark, c); - } - - // add a text range marker if the found position is good - if (game.getBoard().contains(mark) && fieldFire.get(bracket).contains(mark) - && ((bracket > 0) || (numMinMarkers < 2))) { - TextMarkerSprite tS = new TextMarkerSprite(this, mark, - rangeTexts[bracket], FieldofFireSprite.getFieldOfFireColor(bracket)); - fieldOfFireSprites.add(tS); - if (bracket == 0) { - numMinMarkers++; - } - } - } - } - - boardPanel.repaint(); - } - /** Displays a dialog and changes the theme of all * board hexes to the user-chosen theme. */ diff --git a/megamek/src/megamek/client/ui/swing/boardview/FiringArcSpriteHandler.java b/megamek/src/megamek/client/ui/swing/boardview/FiringArcSpriteHandler.java new file mode 100644 index 00000000000..5c6fbb8ae60 --- /dev/null +++ b/megamek/src/megamek/client/ui/swing/boardview/FiringArcSpriteHandler.java @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MegaMek. + * + * MegaMek is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * MegaMek is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MegaMek. If not, see . + */ +package megamek.client.ui.swing.boardview; + +import megamek.client.ui.swing.*; +import megamek.common.*; +import megamek.common.equipment.WeaponMounted; +import megamek.common.options.OptionsConstants; +import megamek.common.preference.IPreferenceChangeListener; +import megamek.common.preference.PreferenceChangeEvent; +import megamek.common.weapons.infantry.InfantryWeapon; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * This BoardViewSpriteHandler handles the sprites for the firing arcs (field of fire) that can be shown for + * individual weapons or bays. The field of fire depends on a handful of variables (position, unit facing + * and twists, weapon arc and range). These variables can and must be set individually and are cached. + */ +public class FiringArcSpriteHandler extends BoardViewSpriteHandler implements IPreferenceChangeListener { + + private static final String[] rangeTexts = {"min", "S", "M", "L", "E"}; + + private final Game game; + private final ClientGUI clientGUI; + + // In this handler, the values are cached because only some of the values are updated by method calls at a time + private Entity firingEntity; + private Coords firingPosition; + private int facing; + private int arc; + private boolean isUnderWater = false; + private final int[][] ranges = new int[2][5]; + + public FiringArcSpriteHandler(BoardView boardView, ClientGUI clientGUI) { + super(boardView); + this.clientGUI = clientGUI; + game = clientGUI.getClient().getGame(); + } + + /** + * Updates the facing that is used for aligning the field of fire. + * Does not change the assumed unit, position weapon and arc. + * + * @param facing The unit's facing + */ + public void updateFacing(int facing) { + this.facing = facing; + renewSprites(); + } + + /** + * Updates the position that is used for centering the field of fire to the given Coords. + * Does not change the assumed unit, weapon and arc, nor the facing. + * + * @param firingPosition The position to center the field of fire on + */ + public void updatePosition(Coords firingPosition) { + this.firingPosition = firingPosition; + renewSprites(); + } + + /** + * Updates the position and facing that is used for centering the field of fire to the end + * position and facing of the given movement path. Does not change the assumed unit, weapon and arc. + * + * @param movePath The considered movement path + */ + public void updatePosition(MovePath movePath) { + firingPosition = movePath.getFinalCoords(); + facing = movePath.getFinalFacing(); + isUnderWater=testUnderWater(movePath); + renewSprites(); + } + + /** + * Sets the selected unit and weapon. This will recalculate ranges, the facing including possible + * torso/turret twists and the weapon arc. When no firing position is currently stored, the unit's + * position will be used. This method does not check if the weapon is valid or, actually, on the unit. + * + * @param firingEntity the unit carrying the weapon to consider + * @param weapon the weapon to consider + */ + public void updateSelectedWeapon(Entity firingEntity, WeaponMounted weapon) { + this.firingEntity = firingEntity; + arc = firingEntity.getWeaponArc(clientGUI.getSelectedWeaponId()); + findRanges(weapon); + updateFacing(weapon); + if (firingPosition == null) { + firingPosition = firingEntity.getPosition(); + } + renewSprites(); + } + + /** + * Clears the sprites and resets the cached values so no new sprites are drawn at this time. As this + * handler requires cached values for weapon, arc etc. to draw the sprites, the {@link #clear()} method + * may not be overridden to reset those fields as is done in other handlers. + */ + public void clearValues() { + clear(); + firingEntity = null; + firingPosition = null; + isUnderWater = false; + } + + /** + * Draw the sprites for the currently stored values for position, unit, arc etc. Does not draw sprites + * if field of fire is deactivated. + */ + public void renewSprites() { + clear(); + if (!GUIP.getShowFieldOfFire() || (firingEntity == null) || (firingPosition == null) + || firingEntity.isOffBoard()) { + return; + } + + // check if extreme range is used + int maxrange = 4; + if (!game.getBoard().onGround() || game.getOptions().booleanOption(OptionsConstants.ADVCOMBAT_TACOPS_RANGE)) { + maxrange = 5; + } + + // create the lists of hexes + List> fieldFire = new ArrayList<>(5); + int range = 1; + // for all available range brackets Min/S/M/L/E ... + for (int bracket = 0; bracket < maxrange; bracket++) { + fieldFire.add(new HashSet<>()); + // Add all hexes up to the weapon range to separate lists + while (range <= ranges[underWaterIndex()][bracket]) { + fieldFire.get(bracket).addAll(firingPosition.allAtDistance(range)); + range++; + if (range > 100) { + break; // only to avoid hangs + } + } + + // Remove hexes that are not on the board or not in the arc + fieldFire.get(bracket).removeIf(h -> !game.getBoard().contains(h) || !Compute.isInArc(firingPosition, facing, h, arc)); + } + + // for all available range brackets Min/S/M/L/E ... + for (int bracket = 0; bracket < fieldFire.size(); bracket++) { + if (fieldFire.get(bracket) == null) { + continue; + } + for (Coords loc : fieldFire.get(bracket)) { + // check surrounding hexes + int edgesToPaint = 0; + for (int dir = 0; dir < 6; dir++) { + Coords adjacentHex = loc.translated(dir); + if (!fieldFire.get(bracket).contains(adjacentHex)) { + edgesToPaint += (1 << dir); + } + } + // create sprite if there's a border to paint + if (edgesToPaint > 0) { + FieldofFireSprite ffSprite = new FieldofFireSprite(boardView, bracket, loc, edgesToPaint); + currentSprites.add(ffSprite); + } + } + // Add range markers (m, S, M, L, E) + // this looks for a hex in the middle of the range bracket; + // if outside the board, nearer hexes will be tried until + // the inner edge of the range bracket is reached + // the directions tested are those that fall between the + // hex facings because this makes for a better placement + // ... most of the time... + + // The directions[][] is used to make the marker placement + // fairly symmetrical to the unit facing which a simple for + // loop over the hex facings doesn't do + int[][] directions = {{0, 1}, {0, 5}, {3, 2}, {3, 4}, {1, 2}, {5, 4}}; + // don't paint too many "min" markers + int numMinMarkers = 0; + for (int[] dir : directions) { + // find the middle of the range bracket + int rangeend = Math.max(ranges[underWaterIndex()][bracket], 0); + int rangebegin = 1; + if (bracket > 0) { + rangebegin = Math.max(ranges[underWaterIndex()][bracket - 1] + 1, 1); + } + int dist = (rangeend + rangebegin) / 2; + // translate to the middle of the range bracket + Coords mark = firingPosition.translated((dir[0] + facing) % 6, (dist + 1) / 2) + .translated((dir[1] + facing) % 6, dist / 2); + // traverse back to the unit until a hex is onboard + while (!game.getBoard().contains(mark)) { + mark = Coords.nextHex(mark, firingPosition); + } + + // add a text range marker if the found position is good + if (game.getBoard().contains(mark) && fieldFire.get(bracket).contains(mark) + && ((bracket > 0) || (numMinMarkers < 2))) { + TextMarkerSprite tS = new TextMarkerSprite(boardView, mark, + rangeTexts[bracket], FieldofFireSprite.getFieldOfFireColor(bracket)); + currentSprites.add(tS); + if (bracket == 0) { + numMinMarkers++; + } + } + } + } + + boardView.addSprites(currentSprites); + } + + + @Override + public void initialize() { + GUIP.addPreferenceChangeListener(this); + } + + @Override + public void dispose() { + clear(); + GUIP.removePreferenceChangeListener(this); + } + + @Override + public void preferenceChange(PreferenceChangeEvent evt) { + if (evt.getName().equals(GUIPreferences.SHOW_FIELD_OF_FIRE)) { + renewSprites(); + } + } + + /** + * @return The ranges lookup index depending on whether the weapon is underWater + */ + private int underWaterIndex() { + return isUnderWater ? 1 : 0; + } + + private void updateFacing(WeaponMounted weapon) { + facing = firingEntity.getFacing(); + if (game.getPhase().isFiring()) { + if (firingEntity.isSecondaryArcWeapon(firingEntity.getEquipmentNum(weapon))) { + facing = firingEntity.getSecondaryFacing(); + } + // If this is mech with turrets, check to see if the weapon is on a turret. + if ((firingEntity instanceof Mech) && (weapon.isMechTurretMounted())) { + // facing is currently adjusted for mek torso twist and facing, adjust for turret facing. + facing = (weapon.getFacing() + facing) % 6; + } + // If this is a tank with dual turrets, check to see if the weapon is a second turret. + if ((firingEntity instanceof Tank) && (weapon.getLocation() == ((Tank) firingEntity).getLocTurret2())) { + facing = ((Tank) firingEntity).getDualTurretFacing(); + } + } else if (game.getPhase().isTargeting()) { + if (firingEntity.isSecondaryArcWeapon(firingEntity.getEquipmentNum(weapon))) { + facing = firingEntity.getSecondaryFacing(); + } + } + } + + private void findRanges(WeaponMounted weapon) { + WeaponType wtype = weapon.getType(); + ranges[0] = wtype.getRanges(weapon); + + AmmoType atype = null; + if ((weapon.getLinked() != null) && (weapon.getLinked().getType() instanceof AmmoType)) { + atype = (AmmoType) weapon.getLinked().getType(); + } + + // gather underwater ranges + ranges[1] = wtype.getWRanges(); + if (atype != null) { + if ((wtype.getAmmoType() == AmmoType.T_SRM) + || (wtype.getAmmoType() == AmmoType.T_SRM_IMP) + || (wtype.getAmmoType() == AmmoType.T_MRM) + || (wtype.getAmmoType() == AmmoType.T_LRM) + || (wtype.getAmmoType() == AmmoType.T_LRM_IMP) + || (wtype.getAmmoType() == AmmoType.T_MML)) { + if (atype.getMunitionType().contains(AmmoType.Munitions.M_TORPEDO)) { + ranges[1] = wtype.getRanges(weapon); + } else if (atype.getMunitionType().contains(AmmoType.Munitions.M_MULTI_PURPOSE)) { + ranges[1] = wtype.getRanges(weapon); + } + } + } + + // Infantry range types 4+ are simplified to + // the usual range types as displaying 5 range circles + // would be visual overkill (and besides this makes + // things easier) + if (wtype instanceof InfantryWeapon) { + InfantryWeapon inftype = (InfantryWeapon) wtype; + int iR = inftype.getInfantryRange(); + ranges[0] = new int[] { 0, iR, iR * 2, iR * 3, 0 }; + ranges[1] = new int[] { 0, iR / 2, (iR / 2) * 2, (iR / 2) * 3, 0 }; + } + + // Artillery gets fixed ranges, 100 as an arbitrary + // large range for the targeting phase and + // 6 to 17 in the other phases as it will be + // direct fire then + if (wtype.hasFlag(WeaponType.F_ARTILLERY)) { + boolean isADA = (weapon.getLinked() != null + && ((AmmoType) weapon.getLinked().getType()).getMunitionType().contains(AmmoType.Munitions.M_ADA)); + if (game.getPhase().isTargeting()) { + ranges[0] = (!isADA? new int[] { 0, 0, 0, 100, 0 } : new int[] { 0, 0, 0, 51, 0 }); + } else { + ranges[0] = (!isADA? new int[] { 6, 0, 0, 17, 0 } : wtype.getRanges(weapon)); + } + ranges[1] = new int[] { 0, 0, 0, 0, 0 }; + } + + // Override for the MML ammos + if (atype != null) { + if (atype.getAmmoType() == AmmoType.T_MML) { + if (atype.hasFlag(AmmoType.F_MML_LRM)) { + if (atype.getMunitionType().contains(AmmoType.Munitions.M_DEAD_FIRE)) { + ranges[0] = new int[]{4, 5, 10, 15, 20}; + } else { + ranges[0] = new int[]{6, 7, 14, 21, 28}; + } + } else { + if (atype.getMunitionType().contains(AmmoType.Munitions.M_DEAD_FIRE)) { + ranges[0] = new int[]{0, 2, 4, 6, 8}; + } else { + ranges[0] = new int[]{0, 3, 6, 9, 12}; + } + } + } + } + + // No minimum range for hotload + if ((weapon.getLinked() != null) && weapon.getLinked().isHotLoaded()) { + ranges[0][0] = 0; + } + + // Aero + if (firingEntity.isAirborne()) { + + // keep original ranges ranges, no underwater + ranges[1] = new int[] { 0, 0, 0, 0, 0 }; + int maxr; + + // In the WeaponPanel, when the weapon is out of ammo + // or otherwise nonfunctional, SHORT range will be listed; + // the field of fire is instead disabled + // Here as well as in WeaponPanel, choosing a specific ammo + // only works for the current player's units + if (!weapon.isBreached() && !weapon.isMissing() + && !weapon.isDestroyed() && !weapon.isJammed() + && ((weapon.getLinked() == null) + || (weapon.getLinked().getUsableShotsLeft() > 0))) { + maxr = wtype.getMaxRange(weapon); + + // set the standard ranges, depending on capital or no + // boolean isCap = wtype.isCapital(); + int rangeMultiplier = wtype.isCapital() ? 2 : 1; + if (game.getBoard().onGround()) { + rangeMultiplier *= 8; + } + + for (int rangeIndex = RangeType.RANGE_MINIMUM; rangeIndex <= RangeType.RANGE_EXTREME; rangeIndex++) { + if (maxr >= rangeIndex) { + ranges[0][rangeIndex] = WeaponType.AIRBORNE_WEAPON_RANGES[rangeIndex] * rangeMultiplier; + } + } + } + } + } + + /** + * @return True when, for the given movement path, the currently selected weapon ends up being underwater. + * @param movePath The movement path that is considered for the selected unit + */ + private boolean testUnderWater(MovePath movePath) { + if ((firingEntity == null) || (movePath == null) || !clientGUI.hasSelectedWeapon()) { + return false; + } + + int location = clientGUI.getSelectedWeapon().getLocation(); + Hex hex = game.getBoard().getHex(movePath.getFinalCoords()); + int waterDepth = hex.terrainLevel(Terrains.WATER); + + if ((waterDepth > 0) && !movePath.isJumping() && (movePath.getFinalElevation() < 0)) { + if ((firingEntity instanceof Mech) && !firingEntity.isProne() && (waterDepth == 1)) { + return firingEntity.locationIsLeg(location); + } else { + return true; + } + } + return false; + } +} diff --git a/megamek/src/megamek/client/ui/swing/unitDisplay/UnitDisplay.java b/megamek/src/megamek/client/ui/swing/unitDisplay/UnitDisplay.java index fe157eb62c3..6cb0a6338a4 100644 --- a/megamek/src/megamek/client/ui/swing/unitDisplay/UnitDisplay.java +++ b/megamek/src/megamek/client/ui/swing/unitDisplay/UnitDisplay.java @@ -437,11 +437,14 @@ protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boo * Displays the specified entity in the panel. */ public void displayEntity(Entity en) { - if (en == null) { + if ((en == null) || (currentlyDisplaying == en)) { return; } currentlyDisplaying = en; updateDisplay(); + if (clientgui != null) { + clientgui.clearFieldOfFire(); + } } protected void updateDisplay() { diff --git a/megamek/src/megamek/client/ui/swing/unitDisplay/WeaponPanel.java b/megamek/src/megamek/client/ui/swing/unitDisplay/WeaponPanel.java index a34477ba9df..8fd881b8bd3 100644 --- a/megamek/src/megamek/client/ui/swing/unitDisplay/WeaponPanel.java +++ b/megamek/src/megamek/client/ui/swing/unitDisplay/WeaponPanel.java @@ -2013,177 +2013,12 @@ private void displaySelected() { } // send event to other parts of the UI which care - if (oldmount.isInWaypointLaunchMode()) { - setFieldOfFire(oldmount); - } else { - setFieldOfFire(mounted); - } + unitDisplay.getClientGUI().showSensorRanges(entity); unitDisplay.processMechDisplayEvent(new MechDisplayEvent(this, entity, mounted)); onResize(); addListeners(); } - // this gathers all the range data - // it is a boiled down version of displaySelected, - // updateAttackValues, updateRangeDisplayForAmmo - private void setFieldOfFire(WeaponMounted mounted) { - // No field of fire without ClientGUI - if (unitDisplay.getClientGUI() == null) { - return; - } - - ClientGUI gui = unitDisplay.getClientGUI(); - - WeaponType wtype = mounted.getType(); - int[][] ranges = new int[2][5]; - ranges[0] = wtype.getRanges(mounted); - - AmmoType atype = null; - if ((mounted.getLinked() != null) && (mounted.getLinked().getType() instanceof AmmoType)) { - atype = (AmmoType) mounted.getLinked().getType(); - } - - // gather underwater ranges - ranges[1] = wtype.getWRanges(); - if (atype != null) { - if ((wtype.getAmmoType() == AmmoType.T_SRM) - || (wtype.getAmmoType() == AmmoType.T_SRM_IMP) - || (wtype.getAmmoType() == AmmoType.T_MRM) - || (wtype.getAmmoType() == AmmoType.T_LRM) - || (wtype.getAmmoType() == AmmoType.T_LRM_IMP) - || (wtype.getAmmoType() == AmmoType.T_MML)) { - if (atype.getMunitionType().contains(AmmoType.Munitions.M_TORPEDO)) { - ranges[1] = wtype.getRanges(mounted); - } else if (atype.getMunitionType().contains(AmmoType.Munitions.M_MULTI_PURPOSE)) { - ranges[1] = wtype.getRanges(mounted); - } - } - } - - // Infantry range types 4+ are simplified to - // the usual range types as displaying 5 range circles - // would be visual overkill (and besides this makes - // things easier) - if (wtype instanceof InfantryWeapon) { - InfantryWeapon inftype = (InfantryWeapon) wtype; - int iR = inftype.getInfantryRange(); - ranges[0] = new int[] { 0, iR, iR * 2, iR * 3, 0 }; - ranges[1] = new int[] { 0, iR / 2, (iR / 2) * 2, (iR / 2) * 3, 0 }; - } - - // Artillery gets fixed ranges, 100 as an arbitrary - // large range for the targeting phase and - // 6 to 17 in the other phases as it will be - // direct fire then - if (wtype.hasFlag(WeaponType.F_ARTILLERY)) { - boolean isADA = (mounted.getLinked() != null - && ((AmmoType) mounted.getLinked().getType()).getMunitionType().contains(AmmoType.Munitions.M_ADA)); - if (gui.getCurrentPanel() instanceof TargetingPhaseDisplay) { - ranges[0] = (!isADA? new int[] { 0, 0, 0, 100, 0 } : new int[] { 0, 0, 0, 51, 0 }); - } else { - ranges[0] = (!isADA? new int[] { 6, 0, 0, 17, 0 } : wtype.getRanges(mounted)); - } - ranges[1] = new int[] { 0, 0, 0, 0, 0 }; - } - - // Override for the MML ammos - if (atype != null) { - if (atype.getAmmoType() == AmmoType.T_MML) { - if (atype.hasFlag(AmmoType.F_MML_LRM)) { - if (atype.getMunitionType().contains(AmmoType.Munitions.M_DEAD_FIRE)) { - ranges[0] = new int[]{4, 5, 10, 15, 20}; - } else { - ranges[0] = new int[]{6, 7, 14, 21, 28}; - } - } else { - if (atype.getMunitionType().contains(AmmoType.Munitions.M_DEAD_FIRE)) { - ranges[0] = new int[]{0, 2, 4, 6, 8}; - } else { - ranges[0] = new int[]{0, 3, 6, 9, 12}; - } - } - } - } - - // No minimum range for hotload - if ((mounted.getLinked() != null) && mounted.getLinked().isHotLoaded()) { - ranges[0][0] = 0; - } - - // Aero - if (entity.isAirborne()) { - - // keep original ranges ranges, no underwater - ranges[1] = new int[] { 0, 0, 0, 0, 0 }; - int maxr = WeaponType.RANGE_SHORT; - - // In the WeaponPanel, when the weapon is out of ammo - // or otherwise nonfunctional, SHORT range will be listed; - // the field of fire is instead disabled - // Here as well as in WeaponPanel, choosing a specific ammo - // only works for the current player's units - if (!mounted.isBreached() && !mounted.isMissing() - && !mounted.isDestroyed() && !mounted.isJammed() - && ((mounted.getLinked() == null) - || (mounted.getLinked().getUsableShotsLeft() > 0))) { - maxr = wtype.getMaxRange(mounted); - - // set the standard ranges, depending on capital or no - // boolean isCap = wtype.isCapital(); - int rangeMultiplier = wtype.isCapital() ? 2 : 1; - final Game game = unitDisplay.getClientGUI().getClient().getGame(); - if (game.getBoard().onGround()) { - rangeMultiplier *= 8; - } - - for (int rangeIndex = RangeType.RANGE_MINIMUM; rangeIndex <= RangeType.RANGE_EXTREME; rangeIndex++) { - if (maxr >= rangeIndex) { - ranges[0][rangeIndex] = WeaponType.AIRBORNE_WEAPON_RANGES[rangeIndex] * rangeMultiplier; - } - } - } - } - - // pass all this to the Displays - int weaponId = entity.getEquipmentNum(mounted); - int arc = entity.getWeaponArc(weaponId); - int loc = mounted.getLocation(); - - if (gui.getCurrentPanel() instanceof FiringDisplay) { - // twisting - int facing = entity.getFacing(); - if (entity.isSecondaryArcWeapon(entity.getEquipmentNum(mounted))) { - facing = entity.getSecondaryFacing(); - } - // If this is mech with turrets, check to see if the weapon is on a turret. - if ((entity instanceof Mech) && (entity.getEquipment(weaponId).isMechTurretMounted())) { - // facing is currently adjusted for mek toro twist and facing, adjust for turret facing. - facing = (mounted.getFacing() + facing) % 6; - } - // If this is a tank with dual turrets, check to see if the weapon is a second turret. - if ((entity instanceof Tank) && - (entity.getEquipment(weaponId).getLocation() == ((Tank) entity).getLocTurret2())) { - facing = ((Tank) entity).getDualTurretFacing(); - } - ((FiringDisplay) gui.getCurrentPanel()).setWeaponFieldOfFire(entity, ranges, arc, loc, facing); - } else if (gui.getCurrentPanel() instanceof TargetingPhaseDisplay) { - // twisting - int facing = entity.getFacing(); - if (entity.isSecondaryArcWeapon(entity.getEquipmentNum(mounted))) { - facing = entity.getSecondaryFacing(); - } - ((TargetingPhaseDisplay) gui.getCurrentPanel()).setWeaponFieldOfFire(entity, ranges, arc, loc, facing); - } else if (gui.getCurrentPanel() instanceof MovementDisplay) { - // no twisting here - ((MovementDisplay) gui.getCurrentPanel()).setWeaponFieldOfFire(entity, ranges, arc, loc); - } else if (gui.getCurrentPanel() instanceof DeploymentDisplay) { - // no twisting here - ((DeploymentDisplay) gui.getCurrentPanel()).setWeaponFieldOfFire(entity, ranges, arc, loc); - } - - unitDisplay.getClientGUI().showSensorRanges(entity); - } - private String formatAmmo(Mounted m) { StringBuffer sb = new StringBuffer(64); int ammoIndex = m.getDesc().indexOf(Messages.getString("MechDisplay.0")); @@ -2711,7 +2546,6 @@ public void valueChanged(ListSelectionEvent event) { // Tell the Display to update the // firing arc info when a weapon has been de-selected if (weaponList.getSelectedIndex() == -1) { - unitDisplay.getClientGUI().getBoardView().clearFieldOfFire(); unitDisplay.getClientGUI().clearTemporarySprites(); } } diff --git a/megamek/src/megamek/common/Mounted.java b/megamek/src/megamek/common/Mounted.java index fc7ce2a9060..437f540e0f3 100644 --- a/megamek/src/megamek/common/Mounted.java +++ b/megamek/src/megamek/common/Mounted.java @@ -1494,40 +1494,32 @@ public void setModeSwitchable(boolean b) { } /** - * Method that checks to see if our capital missile bay is in bearings-only mode + * @return True if our capital missile bay is in bearings-only mode * Only available in space games - * @return */ public boolean isInBearingsOnlyMode() { - if ((curMode().equals(Weapon.MODE_CAP_MISSILE_BEARING_EXT) - || curMode().equals(Weapon.MODE_CAP_MISSILE_BEARING_LONG) - || curMode().equals(Weapon.MODE_CAP_MISSILE_BEARING_MED) - || curMode().equals(Weapon.MODE_CAP_MISSILE_BEARING_SHORT) - || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_EXT) - || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_LONG) - || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_MED) - || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_SHORT)) - && getEntity().isSpaceborne()) { - return true; - } - return false; + return (curMode().equals(Weapon.MODE_CAP_MISSILE_BEARING_EXT) + || curMode().equals(Weapon.MODE_CAP_MISSILE_BEARING_LONG) + || curMode().equals(Weapon.MODE_CAP_MISSILE_BEARING_MED) + || curMode().equals(Weapon.MODE_CAP_MISSILE_BEARING_SHORT) + || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_EXT) + || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_LONG) + || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_MED) + || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_SHORT)) + && getEntity().isSpaceborne(); } /** - * Method that checks to see if our capital missile bay is in waypoint launch mode + * @return True if our capital missile bay is in waypoint launch mode * Only available in space games - * @return */ public boolean isInWaypointLaunchMode() { - if ((curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_EXT) + return (curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_EXT) || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_LONG) || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_MED) || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT_BEARING_SHORT) || curMode().equals(Weapon.MODE_CAP_MISSILE_WAYPOINT)) - && getEntity().isSpaceborne()) { - return true; - } - return false; + && getEntity().isSpaceborne(); } /** From 7354ccff48c9aa23d2373e3fcd8dee58aeff9275 Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 12 May 2024 15:52:16 +0200 Subject: [PATCH 08/11] misc cleanup --- .../megamek/client/ui/swing/ClientGUI.java | 26 +++++++++++++++++++ .../client/ui/swing/boardview/BoardView.java | 7 ----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java index d61b7f59b48..33d1ae824bf 100644 --- a/megamek/src/megamek/client/ui/swing/ClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java @@ -2932,6 +2932,14 @@ public JPanel getMainPanel() { return clientGuiPanel; } + /** + * Updates the position used for showing the field of fire to the end point of the given movepath. This + * method does nothing if the given entity is not the one viewed in the unit viewer (and therefore + * not the one for which a weapon field of fire is currently shown). + * + * @param entity The unit to take the facing from if it is the currently viewed unit + * @param movePath A planned movement path to take the end position from + */ public void setFiringArcPosition(Entity entity, MovePath movePath) { // Only update when the selected weapon is on the unit that's moving if (entity.getId() == getSelectedEntityNum()) { @@ -2939,6 +2947,14 @@ public void setFiringArcPosition(Entity entity, MovePath movePath) { } } + /** + * Updates the position used for showing the field of fire to the given position. This + * method does nothing if the given entity is not the one viewed in the unit viewer (and therefore + * not the one for which a weapon field of fire is currently shown). + * + * @param entity The unit to take the facing from if it is the currently viewed unit + * @param coords The position to center the field of fire on + */ public void setFiringArcPosition(Entity entity, Coords coords) { // Only update when the selected weapon is on the unit that's changing position if (entity.getId() == getSelectedEntityNum()) { @@ -2946,6 +2962,13 @@ public void setFiringArcPosition(Entity entity, Coords coords) { } } + /** + * Updates the facing used for showing the field of fire to the basic facing of the given unit. This + * method does nothing if the given entity is not the one viewed in the unit viewer (and therefore + * not the one for which a weapon field of fire is currently shown). + * + * @param entity The unit to take the facing from if it is the currently viewed unit + */ public void setFiringArcFacing(Entity entity) { // Only update when the selected weapon is on the unit that's changing its facing if (entity.getId() == getSelectedEntityNum()) { @@ -2953,6 +2976,9 @@ public void setFiringArcFacing(Entity entity) { } } + /** + * Removes the field of fire from the BoardView and clears the cached values in the sprite handler. + */ public void clearFieldOfFire() { firingArcSpriteHandler.clearValues(); } diff --git a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java index 4e92df821c2..4838dcfb430 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/BoardView.java +++ b/megamek/src/megamek/client/ui/swing/boardview/BoardView.java @@ -5000,13 +5000,6 @@ public boolean shouldFovDarken() { return GUIP.getFovDarken() && !(game.getPhase().isReport()); } - public boolean shouldShowFieldOfFire() { - return GUIP.getShowFieldOfFire() && - (game.getPhase().isDeployment() || game.getPhase().isMovement() - || game.getPhase().isTargeting() || game.getPhase().isFiring() - || game.getPhase().isOffboard()); - } - public void setShowLobbyPlayerDeployment(boolean b) { showLobbyPlayerDeployment = b; } From e32fc20563026424e1a1d0776f6dc128ad53e891 Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 12 May 2024 16:09:10 +0200 Subject: [PATCH 09/11] update field of fire sprites when color settings are changed --- .../ui/swing/boardview/FiringArcSpriteHandler.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/boardview/FiringArcSpriteHandler.java b/megamek/src/megamek/client/ui/swing/boardview/FiringArcSpriteHandler.java index 5c6fbb8ae60..d6db776218f 100644 --- a/megamek/src/megamek/client/ui/swing/boardview/FiringArcSpriteHandler.java +++ b/megamek/src/megamek/client/ui/swing/boardview/FiringArcSpriteHandler.java @@ -130,7 +130,7 @@ public void clearValues() { public void renewSprites() { clear(); if (!GUIP.getShowFieldOfFire() || (firingEntity == null) || (firingPosition == null) - || firingEntity.isOffBoard()) { + || firingEntity.isOffBoard() || !clientGUI.hasSelectedWeapon()) { return; } @@ -239,8 +239,15 @@ public void dispose() { @Override public void preferenceChange(PreferenceChangeEvent evt) { - if (evt.getName().equals(GUIPreferences.SHOW_FIELD_OF_FIRE)) { - renewSprites(); + switch (evt.getName()) { + case GUIPreferences.SHOW_FIELD_OF_FIRE: + case GUIPreferences.BOARD_FIELD_OF_FIRE_EXTREME_COLOR: + case GUIPreferences.BOARD_FIELD_OF_FIRE_LONG_COLOR: + case GUIPreferences.BOARD_FIELD_OF_FIRE_MEDIUM_COLOR: + case GUIPreferences.BOARD_FIELD_OF_FIRE_SHORT_COLOR: + case GUIPreferences.BOARD_FIELD_OF_FIRE_MIN_COLOR: + renewSprites(); + break; } } From 55e2baeee8bf06a199b2fbbba5b28af75d85211d Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 17 May 2024 16:30:36 +0200 Subject: [PATCH 10/11] after-merge changes and CodeQL --- .../client/ui/swing/AbstractPhaseDisplay.java | 2 +- .../client/ui/swing/ActionPhaseDisplay.java | 6 ++-- .../megamek/client/ui/swing/ClientGUI.java | 10 +++--- .../client/ui/swing/MovementDisplay.java | 1 - .../client/ui/swing/ReportDisplay.java | 3 ++ .../ui/swing/StatusBarPhaseDisplay.java | 2 +- .../client/ui/swing/lobby/ChatLounge.java | 2 +- .../strategicBattleSystems/SBFGame.java | 34 ------------------- 8 files changed, 13 insertions(+), 47 deletions(-) diff --git a/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java b/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java index d7f922ae938..bf29073f495 100644 --- a/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java +++ b/megamek/src/megamek/client/ui/swing/AbstractPhaseDisplay.java @@ -33,7 +33,7 @@ public abstract class AbstractPhaseDisplay extends SkinnedJPanel implements protected DistractableAdapter distracted = new DistractableAdapter(); protected MegamekButton butDone; - protected IClientGUI clientgui; + private final IClientGUI clientgui; protected AbstractPhaseDisplay(IClientGUI cg) { this(cg, SkinSpecification.UIComponents.PhaseDisplay.getComp(), diff --git a/megamek/src/megamek/client/ui/swing/ActionPhaseDisplay.java b/megamek/src/megamek/client/ui/swing/ActionPhaseDisplay.java index 5eb0e43bd72..39eb1e399c1 100644 --- a/megamek/src/megamek/client/ui/swing/ActionPhaseDisplay.java +++ b/megamek/src/megamek/client/ui/swing/ActionPhaseDisplay.java @@ -46,7 +46,7 @@ protected ActionPhaseDisplay(ClientGUI cg) { protected UIUtil.FixedXPanel setupDonePanel() { var donePanel = super.setupDonePanel(); butSkipTurn = new MegamekButton("SKIP", SkinSpecification.UIComponents.PhaseDisplayDoneButton.getComp()); - butSkipTurn.setPreferredSize(new Dimension(UIUtil.scaleForGUI(DONE_BUTTON_WIDTH), MIN_BUTTON_SIZE.height * 1)); + butSkipTurn.setPreferredSize(new Dimension(UIUtil.scaleForGUI(DONE_BUTTON_WIDTH), MIN_BUTTON_SIZE.height)); String f = guiScaledFontHTML(UIUtil.uiLightViolet()) + KeyCommandBind.getDesc(KeyCommandBind.DONE_NO_ACTION)+ ""; butSkipTurn.setToolTipText("" + f + ""); addToDonePanel(donePanel, butSkipTurn); @@ -108,7 +108,7 @@ protected void initDonePanelForNewTurn() { } /** called to reset, show, hide and relabel the Done panel buttons. Override to change button labels and states, - * being sure to call {@link #updateDonePanelButtons(String,String,boolean) UpdateDonePanelButtons} + * being sure to call {@link #updateDonePanelButtons(String, String, boolean, List)} * to set the button labels and states */ abstract protected void updateDonePanel(); @@ -349,6 +349,6 @@ protected void updateDonePanelButtons(final String doneButtonLabel, final String } private void adaptToGUIScale() { - butSkipTurn.setPreferredSize(new Dimension(UIUtil.scaleForGUI(DONE_BUTTON_WIDTH), MIN_BUTTON_SIZE.height * 1)); + butSkipTurn.setPreferredSize(new Dimension(UIUtil.scaleForGUI(DONE_BUTTON_WIDTH), MIN_BUTTON_SIZE.height)); } } diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java index 919362374eb..4c702770154 100644 --- a/megamek/src/megamek/client/ui/swing/ClientGUI.java +++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java @@ -350,17 +350,15 @@ public class ClientGUI extends AbstractClientGUI implements BoardViewListener, * clean up after itself as much as possible, but will not call * System.exit(). */ - public ClientGUI(IClient client, MegaMekController c) { - if (!(client instanceof Client)) { - throw new IllegalArgumentException("TW ClientGUI must use TW Client!"); - } - this.client = (Client) client; + public ClientGUI(Client client, MegaMekController c) { + super(client); + this.client = client; controller = c; panMain.setLayout(cardsMain); panSecondary.setLayout(cardsSecondary); clientGuiPanel.setLayout(new BorderLayout()); - clientGuiPanel.addComponentListener(this); + clientGuiPanel.addComponentListener(resizeListener); clientGuiPanel.add(panMain, BorderLayout.CENTER); clientGuiPanel.add(panSecondary, BorderLayout.SOUTH); diff --git a/megamek/src/megamek/client/ui/swing/MovementDisplay.java b/megamek/src/megamek/client/ui/swing/MovementDisplay.java index a311fc6ef09..bf66e3549ba 100644 --- a/megamek/src/megamek/client/ui/swing/MovementDisplay.java +++ b/megamek/src/megamek/client/ui/swing/MovementDisplay.java @@ -351,7 +351,6 @@ public static MoveCommand[] values(int f, GameOptions opts, boolean forwardIni) public MovementDisplay(final ClientGUI clientgui) { super(clientgui); - this.clientgui = clientgui; if (clientgui != null) { clientgui.getClient().getGame().addGameListener(this); clientgui.getBoardView().addBoardViewListener(this); diff --git a/megamek/src/megamek/client/ui/swing/ReportDisplay.java b/megamek/src/megamek/client/ui/swing/ReportDisplay.java index 33ec8eb3541..28af3e4628c 100644 --- a/megamek/src/megamek/client/ui/swing/ReportDisplay.java +++ b/megamek/src/megamek/client/ui/swing/ReportDisplay.java @@ -86,12 +86,15 @@ public String getHotKeyDesc() { private static final String RD_REPORTDISPLAY = "ReportDisplay."; private static final String RD_TOOLTIP = ".tooltip"; + private final ClientGUI clientgui; + /** * Creates and lays out a new movement phase display for the specified * clientgui.getClient(). */ public ReportDisplay(ClientGUI clientgui) { super(clientgui); + this.clientgui = clientgui; if (clientgui == null) { return; diff --git a/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java b/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java index 3e46b6f9dae..3fad3c5e305 100644 --- a/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java +++ b/megamek/src/megamek/client/ui/swing/StatusBarPhaseDisplay.java @@ -54,7 +54,7 @@ public abstract class StatusBarPhaseDisplay extends AbstractPhaseDisplay protected static final GUIPreferences GUIP = GUIPreferences.getInstance(); private static final int BUTTON_ROWS = 2; private static final String SBPD_KEY_CLEARBUTTON = "clearButton"; - protected ClientGUI clientgui; + protected final ClientGUI clientgui; /** * timer that ends turn if time limit set in options is over diff --git a/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java b/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java index 39bbd84b99e..381fabe6696 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java +++ b/megamek/src/megamek/client/ui/swing/lobby/ChatLounge.java @@ -268,7 +268,7 @@ public class ChatLounge extends AbstractPhaseDisplay implements private static final GUIPreferences GUIP = GUIPreferences.getInstance(); - protected ClientGUI clientgui; + private ClientGUI clientgui; /** Creates a new chat lounge for the clientgui.getClient(). */ public ChatLounge(ClientGUI clientgui) { diff --git a/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java b/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java index 41431eaf6f8..e1b67ce6458 100644 --- a/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java +++ b/megamek/src/megamek/common/strategicBattleSystems/SBFGame.java @@ -22,18 +22,15 @@ import megamek.common.alphaStrike.AlphaStrikeElement; import megamek.common.annotations.Nullable; import megamek.common.enums.GamePhase; -import megamek.common.event.GameListener; import megamek.common.event.GamePhaseChangeEvent; import megamek.common.options.GameOptions; import megamek.common.options.OptionsConstants; import megamek.common.planetaryconditions.PlanetaryConditions; import org.apache.logging.log4j.LogManager; -import java.util.*; import java.util.Map; import java.util.Collections; import java.util.List; -import java.util.Map; /** * This is an SBF game's game object that holds all game information. As of 2024, this is under construction. @@ -212,35 +209,4 @@ public void replaceAllReports(Map> newReports) { public void setLastPhase(GamePhase lastPhase) { this.lastPhase = lastPhase; } - - public void setLastPhase(GamePhase lastPhase) { - this.lastPhase = lastPhase; - } - - /** - * Adds the given reports this game's reports. - * - * @param reports the new reports to add - */ - public void addReports(List reports) { - gameReport.add(getCurrentRound(), reports); - } - - @Override - public ReportEntry getNewReport(int messageId) { - return new Report(messageId); - } - - public SBFFullGameReport getGameReport() { - return gameReport; - } - - /** - * Replaces this game's entire reports with the given reports. - * - * @param newReports The new reports to keep as this game's reports - */ - public void replaceAllReports(Map> newReports) { - gameReport.replaceAllReports(newReports); - } } From 637ce46566b445741d1cd38ac9c8148a582ae62f Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 17 May 2024 16:45:49 +0200 Subject: [PATCH 11/11] review changes --- megamek/src/megamek/client/Client.java | 6 +++--- megamek/src/megamek/client/bot/BotClient.java | 2 +- megamek/src/megamek/client/ui/swing/MovementDisplay.java | 1 - megamek/src/megamek/server/GameManager.java | 1 - 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/megamek/src/megamek/client/Client.java b/megamek/src/megamek/client/Client.java index ab3160a393c..c8bb6ff8ca7 100644 --- a/megamek/src/megamek/client/Client.java +++ b/megamek/src/megamek/client/Client.java @@ -694,13 +694,13 @@ protected void receiveAttack(Packet c) { // Should be private? - public String receiveReport(List v) { - if (v == null) { + public String receiveReport(List reports) { + if (reports == null) { return "[null report vector]"; } StringBuffer report = new StringBuffer(); - for (Report r : v) { + for (Report r : reports) { report.append(r.getText()); } diff --git a/megamek/src/megamek/client/bot/BotClient.java b/megamek/src/megamek/client/bot/BotClient.java index 4bd99eece90..0525060cf5c 100644 --- a/megamek/src/megamek/client/bot/BotClient.java +++ b/megamek/src/megamek/client/bot/BotClient.java @@ -1188,7 +1188,7 @@ protected void receiveBuildingCollapse(Packet packet) { * Let's save ourselves a little processing time and not deal with any of it */ @Override - public String receiveReport(List v) { + public String receiveReport(List reports) { return ""; } diff --git a/megamek/src/megamek/client/ui/swing/MovementDisplay.java b/megamek/src/megamek/client/ui/swing/MovementDisplay.java index bf66e3549ba..4ca3b147cca 100644 --- a/megamek/src/megamek/client/ui/swing/MovementDisplay.java +++ b/megamek/src/megamek/client/ui/swing/MovementDisplay.java @@ -1206,7 +1206,6 @@ public void clear() { // clear board cursors clientgui.getBoardView().select(null); clientgui.getBoardView().cursor(null); -// clientgui.clearTemporarySprites(); if (ce == null) { return; diff --git a/megamek/src/megamek/server/GameManager.java b/megamek/src/megamek/server/GameManager.java index b9f422be6ba..1684ec8edf3 100644 --- a/megamek/src/megamek/server/GameManager.java +++ b/megamek/src/megamek/server/GameManager.java @@ -2311,7 +2311,6 @@ protected void endCurrentPhase() { break; case DEPLOYMENT: game.clearDeploymentThisRound(); -// game.checkForCompleteDeployment(); Enumeration pls = game.getPlayers(); while (pls.hasMoreElements()) { Player p = pls.nextElement();