diff --git a/megamek/i18n/megamek/client/messages.properties b/megamek/i18n/megamek/client/messages.properties
index 9fde7efc877..5524f0e6c65 100644
--- a/megamek/i18n/megamek/client/messages.properties
+++ b/megamek/i18n/megamek/client/messages.properties
@@ -475,6 +475,7 @@ BoardView1.Tooltip.Clan=(C)
BoardView1.Tooltip.ClanBrackets=[Clan]
BoardView1.Tooltip.ClanParens=(Clan)
BoardView1.Tooltip.Crippled=CRIPPLED
+BoardView1.Tooltip.Destroyed=DESTROYED
BoardView1.Tooltip.C3=C3
BoardView1.Tooltip.C3CC=C3CC
BoardView1.Tooltip.C3i=C3i
@@ -876,6 +877,7 @@ ClientGUI.FatalError.title=Fatal Error
ClientGUI.FileSaveDialog.title=Select file to save to...
ClientGUI.FileSaveServerDialog.title=Select file to save to...
ClientGUI.FileSaveServerDialog.message=Please enter the filename under which the game will be saved to in the server's savegames directory.
+ClientGUI.ForceDisplay=Force Display
ClientGUI.gameSaveDialogMessage=Do you want to save the game before quitting MegaMek?
ClientGUI.gameSaveFirst=Save First?
ClientGUI.Hide=Hide
@@ -1074,6 +1076,7 @@ CommonMenuBar.viewPlayerSettings=Player Settings
CommonMenuBar.viewClientSettings=Client Settings
CommonMenuBar.viewGameOptions=Game Options...
CommonMenuBar.viewLOSSetting=LOS Setting
+CommonMenuBar.viewForceDisplay=Force Display
CommonMenuBar.viewMekDisplay=Unit Display
CommonMenuBar.viewAccessibilityWindow=Accessibility Window
CommonMenuBar.viewIncGUIScale=Increase GUI Scale
@@ -1201,6 +1204,9 @@ CommonSettingsDialog.colors.UnitTooltipWeaponColor=Weapon Color
CommonSettingsDialog.colors.UnitTooltipQuirkColor=Quirk Color
CommonSettingsDialog.colors.VisualRangeColor=Visual Range Color
CommonSettingsDialog.colors.warningColor=Warning Color
+CommonSettingsDialog.colors.myUnitColor=My Unit Color
+CommonSettingsDialog.colors.allyUnitColor=Ally Unit Color
+CommonSettingsDialog.colors.enemyUnitColor=Enemy Unit Color
CommonSettingsDialog.darkenMapAtNight=Darken Map At Night
CommonSettingsDialog.defaultAutoejectDisabled=Disable automatic ejection by default for units added in the lobby.
CommonSettingsDialog.defaultWeaponSortOrder.tooltip=Sets the default weapon sort order for chassis/models without a specified default.
Changing this setting won't affect games already in progress.
@@ -1862,6 +1868,7 @@ KeyBinds.cmdNames.toggleMinimap=Toggle Minimap
KeyBinds.cmdDesc.toggleMinimap=Turns the Minimap on and off
KeyBinds.cmdNames.viewLosSetting=LOS Settings
KeyBinds.cmdDesc.viewLosSetting=Shows the LOS settings dialog
+KeyBinds.cmdNames.toggleForceDisplay=Toggle Force Display
KeyBinds.cmdNames.toggleUnitDisplay=Toggle Unit Display
KeyBinds.cmdDesc.toggleUnitDisplay=Turns the unit display window on and off
KeyBinds.cmdNames.toggleUnitOverview=Toggle Unit Overview
diff --git a/megamek/mmconf/defaultKeyBinds.xml b/megamek/mmconf/defaultKeyBinds.xml
index d9a694d32da..43ebc238831 100644
--- a/megamek/mmconf/defaultKeyBinds.xml
+++ b/megamek/mmconf/defaultKeyBinds.xml
@@ -470,8 +470,15 @@
- undoIllegalSteps
- 8
+ undoIllegalSteps
+ 8
+ 128
+ false
+
+
+
+ toggleForceDisplay
+ 70
128
false
diff --git a/megamek/src/megamek/client/ui/swing/ClientGUI.java b/megamek/src/megamek/client/ui/swing/ClientGUI.java
index fa8b123886f..40ef9ae80c7 100644
--- a/megamek/src/megamek/client/ui/swing/ClientGUI.java
+++ b/megamek/src/megamek/client/ui/swing/ClientGUI.java
@@ -34,6 +34,8 @@
import megamek.client.ui.swing.boardview.BoardView;
import megamek.client.ui.swing.dialog.AbstractUnitSelectorDialog;
import megamek.client.ui.swing.dialog.MegaMekUnitSelectorDialog;
+import megamek.client.ui.swing.forceDisplay.ForceDisplayDialog;
+import megamek.client.ui.swing.forceDisplay.ForceDisplayPanel;
import megamek.client.ui.swing.lobby.ChatLounge;
import megamek.client.ui.swing.lobby.PlayerSettingsDialog;
import megamek.client.ui.swing.minimap.Minimap;
@@ -146,6 +148,7 @@ public class ClientGUI extends JPanel implements BoardViewListener,
// region view menu
public static final String VIEW_INCGUISCALE = "viewIncGUIScale";
public static final String VIEW_DECGUISCALE = "viewDecGUIScale";
+ public static final String VIEW_FORCE_DISPLAY = "viewForceDisplay";
public static final String VIEW_UNIT_DISPLAY = "viewMekDisplay";
public static final String VIEW_ACCESSIBILITY_WINDOW = "viewAccessibilityWindow";
public static final String VIEW_KEYBINDS_OVERLAY = "viewKeyboardShortcuts";
@@ -242,6 +245,10 @@ public class ClientGUI extends JPanel implements BoardViewListener,
public UnitDisplay unitDisplay;
private UnitDisplayDialog unitDisplayDialog;
+
+ public ForceDisplayPanel forceDisplayPanel;
+ private ForceDisplayDialog forceDisplayDialog;
+
public JDialog minimapW;
private MapMenu popup;
private UnitOverview uo;
@@ -373,6 +380,22 @@ public void setUnitDisplayDialog(final UnitDisplayDialog unitDisplayDialog) {
this.unitDisplayDialog = unitDisplayDialog;
}
+ public ForceDisplayPanel getForceDisplayPanel() {
+ return forceDisplayPanel;
+ }
+
+ public void setForceDisplayPanel(final ForceDisplayPanel forceDisplayPanel) {
+ this.forceDisplayPanel = forceDisplayPanel;
+ }
+
+ public ForceDisplayDialog getForceDisplayDialog() {
+ return forceDisplayDialog;
+ }
+
+ public void setForceDisplayDialog(final ForceDisplayDialog forceDisplayDialog) {
+ this.forceDisplayDialog = forceDisplayDialog;
+ }
+
public JDialog getMiniMapDialog() {
return minimapW;
}
@@ -578,6 +601,11 @@ public void windowClosing(WindowEvent e) {
setUnitDisplayDialog(new UnitDisplayDialog(getFrame(), this));
getUnitDisplayDialog().setVisible(false);
+ setForceDisplayPanel(new ForceDisplayPanel(this));
+ setForceDisplayDialog(new ForceDisplayDialog(getFrame(), this));
+ getForceDisplayDialog().add(getForceDisplayPanel(), BorderLayout.CENTER);
+ getForceDisplayDialog().setVisible(false);
+
setMiniReportDisplay(new MiniReportDisplay(this));
setMiniReportDisplayDialog(new MiniReportDisplayDialog(getFrame(), this));
getMiniReportDisplayDialog().setVisible(false);
@@ -740,6 +768,9 @@ public void resetWindowPositions() {
if (getUnitDisplayDialog() != null) {
getUnitDisplayDialog().setBounds(0, 0, getUnitDisplay().getWidth(), getUnitDisplay().getHeight());
}
+ if (getForceDisplayDialog() != null) {
+ getForceDisplayDialog().setBounds(0, 0, getForceDisplayPanel().getWidth(), getForceDisplayPanel().getHeight());
+ }
if (getMiniReportDisplayDialog() != null) {
getMiniReportDisplayDialog().setBounds(0, 0, getMiniReportDisplayDialog().getWidth(),
getMiniReportDisplayDialog().getHeight());
@@ -856,6 +887,9 @@ public void actionPerformed(ActionEvent event) {
case VIEW_UNIT_DISPLAY:
GUIP.toggleUnitDisplay();
break;
+ case VIEW_FORCE_DISPLAY:
+ GUIP.toggleForceDisplay();
+ break;
case VIEW_MINI_MAP:
GUIP.toggleMinimapEnabled();
break;
@@ -1037,6 +1071,11 @@ void saveSettings() {
saveSplitPaneLocations();
}
+ // Force Display Dialog
+ if (getForceDisplayDialog() != null) {
+ getForceDisplayDialog().saveSettings();
+ }
+
// Mini Report Dialog
if (getMiniReportDisplayDialog() != null) {
getMiniReportDisplayDialog().saveSettings();
@@ -1186,6 +1225,7 @@ void switchPanel(GamePhase phase) {
maybeShowMinimap();
maybeShowUnitDisplay();
+ maybeShowForceDisplay();
maybeShowMiniReport();
maybeShowPlayerList();
@@ -1572,6 +1612,29 @@ public void maybeShowUnitDisplay() {
}
}
+ /** Shows or hides the Unit Display based on the current menu setting. */
+ public void maybeShowForceDisplay() {
+ GamePhase phase = getClient().getGame().getPhase();
+
+ if (phase.isReport()) {
+ int action = GUIP.getForceDisplayAutoDisplayReportPhase();
+ if (action == GUIPreferences.SHOW) {
+ GUIP.setForceDisplayEnabled(true);
+ } else if (action == GUIPreferences.HIDE) {
+ GUIP.setForceDisplayEnabled(false);
+ }
+ } else if (phase.isOnMap()) {
+ int action = GUIP.getForceDisplayAutoDisplayNonReportPhase();
+ if (action == GUIPreferences.SHOW) {
+ GUIP.setForceDisplayEnabled(true);
+ } else if (action == GUIPreferences.HIDE) {
+ GUIP.setForceDisplayEnabled(false);
+ }
+ } else {
+ GUIP.setForceDisplayEnabled(false);
+ }
+ }
+
/**
* Shows or hides the Unit Display based on the given visible. This works
* independently
@@ -1608,6 +1671,13 @@ private void setsetDividerLocations() {
splitPaneA.setDividerLocation(GUIP.getSplitPaneADividerLocaton());
}
+
+ public void setForceDisplayVisible(boolean visible) {
+ if (getForceDisplayDialog() != null) {
+ getForceDisplayDialog().setVisible(visible);
+ }
+ }
+
private void hideEmptyPanel(JPanel p, JSplitPane sp, Double d) {
boolean b = false;
@@ -2228,6 +2298,7 @@ public void gamePhaseChange(GamePhaseChangeEvent e) {
}
menuBar.setPhase(phase);
+
validate();
cb.moveToEnd();
}
@@ -2879,6 +2950,8 @@ public void preferenceChange(PreferenceChangeEvent e) {
setPlayerListVisible(GUIP.getPlayerListEnabled());
} else if (e.getName().equals(GUIPreferences.UNIT_DISPLAY_ENABLED)) {
setUnitDisplayVisible(GUIP.getUnitDisplayEnabled());
+ } else if (e.getName().equals(GUIPreferences.FORCE_DISPLAY_ENABLED)) {
+ setForceDisplayVisible(GUIP.getForceDisplayEnabled());
} else if (e.getName().equals(GUIPreferences.UNIT_DISPLAY_LOCATION)) {
setUnitDisplayVisible(GUIP.getUnitDisplayEnabled());
} else if (e.getName().equals(GUIPreferences.MINI_REPORT_ENABLED)) {
diff --git a/megamek/src/megamek/client/ui/swing/CommonMenuBar.java b/megamek/src/megamek/client/ui/swing/CommonMenuBar.java
index 5383807dbcc..24c4de921ea 100644
--- a/megamek/src/megamek/client/ui/swing/CommonMenuBar.java
+++ b/megamek/src/megamek/client/ui/swing/CommonMenuBar.java
@@ -107,6 +107,7 @@ public class CommonMenuBar extends JMenuBar implements ActionListener, IPreferen
// The View menu
private JCheckBoxMenuItem viewMinimap = new JCheckBoxMenuItem(getString("CommonMenuBar.viewMinimap"));
private JCheckBoxMenuItem viewMekDisplay = new JCheckBoxMenuItem(getString("CommonMenuBar.viewMekDisplay"));
+ private JCheckBoxMenuItem viewForceDisplay = new JCheckBoxMenuItem(getString("CommonMenuBar.viewForceDisplay"));
private JMenuItem viewAccessibilityWindow = new JMenuItem(getString("CommonMenuBar.viewAccessibilityWindow"));
private JCheckBoxMenuItem viewKeybindsOverlay = new JCheckBoxMenuItem(getString("CommonMenuBar.viewKeyboardShortcuts"));
private JCheckBoxMenuItem viewPlanetaryConditionsOverlay = new JCheckBoxMenuItem(getString("CommonMenuBar.viewPlanetaryConditions"));
@@ -260,6 +261,9 @@ public CommonMenuBar() {
initMenuItem(gamePlayerList, menu, VIEW_PLAYER_LIST);
GUIP.setPlayerListEnabled(false);
gamePlayerList.setSelected(false);
+ initMenuItem(viewForceDisplay, menu, VIEW_FORCE_DISPLAY);
+ GUIP.setForceDisplayEnabled(false);
+ viewForceDisplay.setSelected(false);
menu.addSeparator();
initMenuItem(viewKeybindsOverlay, menu, VIEW_KEYBINDS_OVERLAY);
@@ -341,6 +345,7 @@ private void setKeyBinds() {
viewKeybindsOverlay.setAccelerator(KeyCommandBind.keyStroke(KeyCommandBind.KEY_BINDS));
viewPlanetaryConditionsOverlay.setAccelerator(KeyCommandBind.keyStroke(KeyCommandBind.PLANETARY_CONDITIONS));
viewTurnDetailsOverlay.setAccelerator(KeyCommandBind.keyStroke(KeyCommandBind.TURN_DETAILS));
+ viewForceDisplay.setAccelerator(KeyCommandBind.keyStroke(KeyCommandBind.FORCE_DISPLAY));
viewMekDisplay.setAccelerator(KeyCommandBind.keyStroke(KeyCommandBind.UNIT_DISPLAY));
viewUnitOverview.setAccelerator(KeyCommandBind.keyStroke(KeyCommandBind.UNIT_OVERVIEW));
viewLOSSetting.setAccelerator(KeyCommandBind.keyStroke(KeyCommandBind.LOS_SETTING));
@@ -490,6 +495,7 @@ private synchronized void updateEnabledStates() {
viewMovModEnvelope.setEnabled(isInGameBoardView);
gameRoundReport.setEnabled((isInGame));
viewMekDisplay.setEnabled(isInGameBoardView);
+ viewForceDisplay.setEnabled(isInGameBoardView);
fireSaveWeaponOrder.setEnabled(isInGameBoardView);
}
@@ -538,6 +544,8 @@ public void preferenceChange(PreferenceChangeEvent e) {
setKeyBinds();
} else if (e.getName().equals(GUIPreferences.UNIT_DISPLAY_ENABLED)) {
viewMekDisplay.setSelected(GUIP.getUnitDisplayEnabled());
+ } else if (e.getName().equals(GUIPreferences.FORCE_DISPLAY_ENABLED)) {
+ viewForceDisplay.setSelected(GUIP.getForceDisplayEnabled());
} else if (e.getName().equals(GUIPreferences.MINI_REPORT_ENABLED)) {
gameRoundReport.setSelected(GUIP.getMiniReportEnabled());
} else if (e.getName().equals(GUIPreferences.PLAYER_LIST_ENABLED)) {
diff --git a/megamek/src/megamek/client/ui/swing/CommonSettingsDialog.java b/megamek/src/megamek/client/ui/swing/CommonSettingsDialog.java
index 4dc89834a01..131da058a40 100644
--- a/megamek/src/megamek/client/ui/swing/CommonSettingsDialog.java
+++ b/megamek/src/megamek/client/ui/swing/CommonSettingsDialog.java
@@ -163,6 +163,9 @@ private void moveElement(DefaultListModel srcModel, int srcIndex, int trg
private ColourSelectorButton csbWarningColor;
private ColourSelectorButton csbCautionColor;
private ColourSelectorButton csbPrecautionColor;
+ private ColourSelectorButton csbMyUnitColor;
+ private ColourSelectorButton csbAllyUnitColor;
+ private ColourSelectorButton csbEnemyColor;
ArrayList playerColours;
@@ -354,6 +357,8 @@ private void moveElement(DefaultListModel srcModel, int srcIndex, int trg
private JComboBox miniReportAutoDisplayNonReportCombo;
private JComboBox playerListAutoDisplayReportCombo;
private JComboBox playerListAutoDisplayNonReportCombo;
+ private JComboBox forceDisplayAutoDisplayReportCombo;
+ private JComboBox forceDisplayAutoDisplayNonReportCombo;
// Report
private JTextPane reportKeywordsTextPane;
@@ -1546,6 +1551,20 @@ public Component getListCellRendererComponent(JList> list, Object value, int i
addLineSpacer(comps);
+ row = new ArrayList<>();
+ csbMyUnitColor = new ColourSelectorButton(Messages.getString("CommonSettingsDialog.colors.myUnitColor"));
+ csbMyUnitColor.setColour(GUIP.getMyUnitColor());
+ row.add(csbMyUnitColor);
+ csbAllyUnitColor = new ColourSelectorButton(Messages.getString("CommonSettingsDialog.colors.allyUnitColor"));
+ csbAllyUnitColor.setColour(GUIP.getAllyUnitColor());
+ row.add(csbAllyUnitColor);
+ csbEnemyColor = new ColourSelectorButton(Messages.getString("CommonSettingsDialog.colors.enemyUnitColor"));
+ csbEnemyColor.setColour(GUIP.getEnemyUnitColor());
+ row.add(csbEnemyColor);
+ comps.add(row);
+
+ addLineSpacer(comps);
+
row = new ArrayList<>();
row.add(getPlayerColourPanel());
comps.add(row);
@@ -1866,6 +1885,10 @@ protected void cancelAction() {
csbCautionColor.setColour(GUIP.getCautionColor());
csbPrecautionColor.setColour(GUIP.getPrecautionColor());
+ csbMyUnitColor.setColour(GUIP.getMyUnitColor());
+ csbAllyUnitColor.setColour(GUIP.getAllyUnitColor());
+ csbEnemyColor.setColour(GUIP.getEnemyUnitColor());
+
for (PlayerColourHelper pch : playerColours) {
pch.csb.setColour(GUIP.getColor(pch.pc.getText()));
}
@@ -1930,6 +1953,8 @@ protected void cancelAction() {
miniReportAutoDisplayNonReportCombo.setSelectedItem(GUIP.getMiniReportAutoDisplayNonReportPhase());
playerListAutoDisplayReportCombo.setSelectedItem(GUIP.getPlayerListAutoDisplayReportPhase());
playerListAutoDisplayNonReportCombo.setSelectedItem(GUIP.getPlayerListAutoDisplayNonReportPhase());
+ forceDisplayAutoDisplayReportCombo.setSelectedItem(GUIP.getForceDisplayAutoDisplayReportPhase());
+ forceDisplayAutoDisplayNonReportCombo.setSelectedItem(GUIP.getForceDisplayAutoDisplayNonReportPhase());
csbUnitDisplayHeatLevel1.setColour(GUIP.getUnitDisplayHeatLevel1());
csbUnitDisplayHeatLevel2.setColour(GUIP.getUnitDisplayHeatLevel2());
@@ -2035,6 +2060,10 @@ protected void okAction() {
GUIP.setCautionColor(csbCautionColor.getColour());
GUIP.setPrecautionColor(csbPrecautionColor.getColour());
+ GUIP.setMyUnitColor(csbMyUnitColor.getColour());
+ GUIP.setAllyUnitColor(csbAllyUnitColor.getColour());
+ GUIP.setEnemyUnitColor(csbEnemyColor.getColour());
+
for (PlayerColourHelper pch : playerColours) {
GUIP.setColor(pch.pc.getText(), pch.csb.getColour());
}
@@ -2337,6 +2366,8 @@ protected void okAction() {
GUIP.setMiniReportAutoDisplayNonReportPhase(miniReportAutoDisplayNonReportCombo.getSelectedIndex());
GUIP.setPlayerListAutoDisplayReportPhase(playerListAutoDisplayReportCombo.getSelectedIndex());
GUIP.setPlayerListAutoDisplayNonReportPhase(playerListAutoDisplayNonReportCombo.getSelectedIndex());
+ GUIP.setForceDisplayAutoDisplayReportPhase(forceDisplayAutoDisplayReportCombo.getSelectedIndex());
+ GUIP.setForceDisplayAutoDisplayNonReportPhase(forceDisplayAutoDisplayNonReportCombo.getSelectedIndex());
GUIP.setUnitDisplayHeatColorLevel1(csbUnitDisplayHeatLevel1.getColour());
GUIP.setUnitDisplayHeatColorLevel2(csbUnitDisplayHeatLevel2.getColour());
@@ -2703,132 +2734,114 @@ public void keyTyped(KeyEvent evt) {
return outer;
}
+ private JComboBox createHideShowComboBox(int i) {
+ JComboBox cb = new JComboBox<>();
+ cb.addItem(Messages.getString("ClientGUI.Hide"));
+ cb.addItem(Messages.getString("ClientGUI.Show"));
+ cb.addItem(Messages.getString("ClientGUI.Manual"));
+ cb.setMaximumSize(new Dimension(150, 40));
+ cb.setSelectedIndex(i);
+
+ return cb;
+ }
+
private JPanel getPhasePanel() {
List> comps = new ArrayList<>();
ArrayList row;
- JLabel unitDisplayLabel = new JLabel(Messages.getString("CommonMenuBar.viewMekDisplay"));
row = new ArrayList<>();
+ JLabel unitDisplayLabel = new JLabel(Messages.getString("CommonMenuBar.viewMekDisplay"));
row.add(unitDisplayLabel);
comps.add(row);
-
- JLabel phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.reportPhases") + ": ");
- unitDisplayAutoDisplayReportCombo = new JComboBox<>();
- unitDisplayAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Hide"));
- unitDisplayAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Show"));
- unitDisplayAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Manual"));
- unitDisplayAutoDisplayReportCombo.setMaximumSize(new Dimension(150, 40));
row = new ArrayList<>();
- unitDisplayAutoDisplayReportCombo.setSelectedIndex(GUIP.getUnitDisplayAutoDisplayReportPhase());
+ JLabel phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.reportPhases") + ": ");
row.add(phaseLabel);
+ unitDisplayAutoDisplayReportCombo = createHideShowComboBox(GUIP.getUnitDisplayAutoDisplayReportPhase());
row.add(unitDisplayAutoDisplayReportCombo);
comps.add(row);
-
- phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.nonReportPhases") + ": ");
- unitDisplayAutoDisplayNonReportCombo = new JComboBox<>();
- unitDisplayAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Hide"));
- unitDisplayAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Show"));
- unitDisplayAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Manual"));
- unitDisplayAutoDisplayNonReportCombo.setMaximumSize(new Dimension(150, 40));
row = new ArrayList<>();
- unitDisplayAutoDisplayNonReportCombo.setSelectedIndex(GUIP.getUnitDisplayAutoDisplayNonReportPhase());
+ phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.nonReportPhases") + ": ");
row.add(phaseLabel);
+ unitDisplayAutoDisplayNonReportCombo = createHideShowComboBox(GUIP.getUnitDisplayAutoDisplayNonReportPhase());
row.add(unitDisplayAutoDisplayNonReportCombo);
comps.add(row);
addLineSpacer(comps);
- JLabel miniMapLabel = new JLabel(Messages.getString("CommonMenuBar.viewMinimap"));
row = new ArrayList<>();
+ JLabel miniMapLabel = new JLabel(Messages.getString("CommonMenuBar.viewMinimap"));
row.add(miniMapLabel);
comps.add(row);
-
- phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.reportPhases") + ": ");
- miniMapAutoDisplayReportCombo = new JComboBox<>();
- miniMapAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Hide"));
- miniMapAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Show"));
- miniMapAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Manual"));
- miniMapAutoDisplayReportCombo.setMaximumSize(new Dimension(150, 40));
row = new ArrayList<>();
- miniMapAutoDisplayReportCombo.setSelectedIndex(GUIP.getMinimapAutoDisplayReportPhase());
+ phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.reportPhases") + ": ");
row.add(phaseLabel);
+ miniMapAutoDisplayReportCombo = createHideShowComboBox(GUIP.getMinimapAutoDisplayReportPhase());
row.add(miniMapAutoDisplayReportCombo);
comps.add(row);
-
- phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.nonReportPhases") + ": ");
- miniMapAutoDisplayNonReportCombo = new JComboBox<>();
- miniMapAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Hide"));
- miniMapAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Show"));
- miniMapAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Manual"));
- miniMapAutoDisplayNonReportCombo.setMaximumSize(new Dimension(150, 40));
row = new ArrayList<>();
- miniMapAutoDisplayNonReportCombo.setSelectedIndex(GUIP.getMinimapAutoDisplayNonReportPhase());
+ phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.nonReportPhases") + ": ");
row.add(phaseLabel);
+ miniMapAutoDisplayNonReportCombo = createHideShowComboBox(GUIP.getMiniReportAutoDisplayNonReportPhase());
row.add(miniMapAutoDisplayNonReportCombo);
comps.add(row);
addLineSpacer(comps);
- JLabel miniReportLabel = new JLabel(Messages.getString("CommonMenuBar.viewRoundReport"));
row = new ArrayList<>();
+ JLabel miniReportLabel = new JLabel(Messages.getString("CommonMenuBar.viewRoundReport"));
row.add(miniReportLabel);
comps.add(row);
-
- phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.reportPhases") + ": ");
- miniReportAutoDisplayReportCombo = new JComboBox<>();
- miniReportAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Hide"));
- miniReportAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Show"));
- miniReportAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Manual"));
- miniReportAutoDisplayReportCombo.setMaximumSize(new Dimension(150, 40));
row = new ArrayList<>();
- miniReportAutoDisplayReportCombo.setSelectedIndex(GUIP.getMiniReportAutoDisplayReportPhase());
+ phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.reportPhases") + ": ");
row.add(phaseLabel);
+ miniReportAutoDisplayReportCombo = createHideShowComboBox(GUIP.getMiniReportAutoDisplayReportPhase());
row.add(miniReportAutoDisplayReportCombo);
comps.add(row);
-
- phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.nonReportPhases") + ": ");
- miniReportAutoDisplayNonReportCombo = new JComboBox<>();
- miniReportAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Hide"));
- miniReportAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Show"));
- miniReportAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Manual"));
- miniReportAutoDisplayNonReportCombo.setMaximumSize(new Dimension(150, 40));
row = new ArrayList<>();
- miniReportAutoDisplayNonReportCombo.setSelectedIndex(GUIP.getMiniReportAutoDisplayNonReportPhase());
+ phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.nonReportPhases") + ": ");
row.add(phaseLabel);
+ miniReportAutoDisplayNonReportCombo = createHideShowComboBox(GUIP.getMiniReportAutoDisplayNonReportPhase());
row.add(miniReportAutoDisplayNonReportCombo);
comps.add(row);
addLineSpacer(comps);
- JLabel playerListLabel = new JLabel(Messages.getString("CommonMenuBar.viewPlayerList"));
row = new ArrayList<>();
+ JLabel playerListLabel = new JLabel(Messages.getString("CommonMenuBar.viewPlayerList"));
row.add(playerListLabel);
comps.add(row);
-
- phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.reportPhases") + ": ");
- playerListAutoDisplayReportCombo = new JComboBox<>();
- playerListAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Hide"));
- playerListAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Show"));
- playerListAutoDisplayReportCombo.addItem(Messages.getString("ClientGUI.Manual"));
- playerListAutoDisplayReportCombo.setMaximumSize(new Dimension(150, 40));
row = new ArrayList<>();
- playerListAutoDisplayReportCombo.setSelectedIndex(GUIP.getPlayerListAutoDisplayReportPhase());
+ phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.reportPhases") + ": ");
row.add(phaseLabel);
+ playerListAutoDisplayReportCombo = createHideShowComboBox(GUIP.getPlayerListAutoDisplayReportPhase());
row.add(playerListAutoDisplayReportCombo);
comps.add(row);
-
- phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.nonReportPhases") + ": ");
- playerListAutoDisplayNonReportCombo = new JComboBox<>();
- playerListAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Hide"));
- playerListAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Show"));
- playerListAutoDisplayNonReportCombo.addItem(Messages.getString("ClientGUI.Manual"));
- playerListAutoDisplayNonReportCombo.setMaximumSize(new Dimension(150, 40));
row = new ArrayList<>();
- playerListAutoDisplayNonReportCombo.setSelectedIndex(GUIP.getPlayerListAutoDisplayNonReportPhase());
+ phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.nonReportPhases") + ": ");
row.add(phaseLabel);
+ playerListAutoDisplayNonReportCombo = createHideShowComboBox(GUIP.getPlayerListAutoDisplayNonReportPhase());
row.add(playerListAutoDisplayNonReportCombo);
comps.add(row);
+ addLineSpacer(comps);
+
+ row = new ArrayList<>();
+ JLabel forceDisplayLabel = new JLabel(Messages.getString("CommonMenuBar.viewForceDisplay"));
+ row.add(forceDisplayLabel);
+ comps.add(row);
+ row = new ArrayList<>();
+ phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.reportPhases") + ": ");
+ row.add(phaseLabel);
+ forceDisplayAutoDisplayReportCombo = createHideShowComboBox(GUIP.getForceDisplayAutoDisplayReportPhase());
+ row.add(forceDisplayAutoDisplayReportCombo);
+ comps.add(row);
+ row = new ArrayList<>();
+ phaseLabel = new JLabel(Messages.getString("CommonSettingsDialog.nonReportPhases") + ": ");
+ row.add(phaseLabel);
+ forceDisplayAutoDisplayNonReportCombo = createHideShowComboBox(GUIP.getForceDisplayAutoDisplayNonReportPhase());
+ row.add(forceDisplayAutoDisplayNonReportCombo);
+ comps.add(row);
+
return createSettingsPanel(comps);
}
diff --git a/megamek/src/megamek/client/ui/swing/GUIPreferences.java b/megamek/src/megamek/client/ui/swing/GUIPreferences.java
index 0991b29cc18..30de1e6d33a 100644
--- a/megamek/src/megamek/client/ui/swing/GUIPreferences.java
+++ b/megamek/src/megamek/client/ui/swing/GUIPreferences.java
@@ -129,6 +129,14 @@ public class GUIPreferences extends PreferenceStoreProxy {
public static final String CUSTOM_UNIT_HEIGHT = "CustomUnitDialogSizeHeight";
public static final String CUSTOM_UNIT_WIDTH = "CustomUnitDialogSizeWidth";
+ public static final String FORCE_DISPLAY_POS_X = "ForceDisplayPosX";
+ public static final String FORCE_DISPLAY_POS_Y = "ForcetDisplayPosY";
+ public static final String FORCE_DISPLAY_SIZE_HEIGHT = "ForceDisplaySizeHeight";
+ public static final String FORCE_DISPLAY_SIZE_WIDTH = "ForceDisplaySizeWidth";
+ public static final String FORCE_DISPLAY_AUTO_DISPLAY_REPORT_PHASE = "ForceDisplayAutoDisplayReportPhase";
+ public static final String FORCE_DISPLAY_AUTO_DISPLAY_NONREPORT_PHASE = "ForceDisplayAutoDisplayNonReportPhase";
+ public static final String FORCE_DISPLAY_ENABLED = "ForceDisplayEnabled";
+
public static final String UNIT_DISPLAY_POS_X = "UnitDisplayPosX";
public static final String UNIT_DISPLAY_POS_Y = "UnitDisplayPosY";
public static final String UNIT_DISPLAY_NONTABBED_POS_X = "UnitDisplayNontabbedPosX";
@@ -143,8 +151,8 @@ public class GUIPreferences extends PreferenceStoreProxy {
public static final String UNIT_DISPLAY_SIZE_WIDTH = "UnitDisplaySizeWidth";
public static final String UNIT_DISPLAY_NONTABBED_SIZE_HEIGHT = "UnitDisplayNonTabbedSizeHeight";
public static final String UNIT_DISPLAY_NONTABBED_SIZE_WIDTH = "UnitDisplayNontabbedSizeWidth";
- public static final String UNIT_DISPLAY_AUTO_DISPLAY_REPORT_PHASE = "UnitDisplayAutoDiplayReportPhase";
- public static final String UNIT_DISPLAY_AUTO_DISPLAY_NONREPORT_PHASE = "UnitDisplayAutoDiplayNonReportPhase";
+ public static final String UNIT_DISPLAY_AUTO_DISPLAY_REPORT_PHASE = "UnitDisplayAutoDisplayReportPhase";
+ public static final String UNIT_DISPLAY_AUTO_DISPLAY_NONREPORT_PHASE = "UnitDisplayAutoDisplayNonReportPhase";
public static final String UNIT_DISPLAY_ENABLED = "UnitDisplayEnabled";
public static final String UNIT_DISPLAY_LOCATION = "UnitDisplayLocation";
public static final String UNIT_DISPLAY_HEAT_COLOR_1 = "UnitDisplayHeatColor1";
@@ -264,8 +272,8 @@ public class GUIPreferences extends PreferenceStoreProxy {
public static final String MINI_MAP_ZOOM = "MinimapZoom";
public static final String MINI_MAP_HEIGHT_DISPLAY_MODE = "MinimapHeightDisplayMode";
public static final String MINI_MAP_SYMBOLS_DISPLAY_MODE = "MinimapSymbolsDisplayMode";
- public static final String MINI_MAP_AUTO_DISPLAY_REPORT_PHASE = "MinimapAutoDiplayReportPhase";
- public static final String MINI_MAP_AUTO_DISPLAY_NONREPORT_PHASE = "MinimapAutoDiplayNonReportPhase";
+ public static final String MINI_MAP_AUTO_DISPLAY_REPORT_PHASE = "MinimapAutoDisplayReportPhase";
+ public static final String MINI_MAP_AUTO_DISPLAY_NONREPORT_PHASE = "MinimapAutoDisplayNonReportPhase";
public static final String MINIMUM_SIZE_HEIGHT = "MinimumSizeHeight";
public static final String MINIMUM_SIZE_WIDTH = "MinimumSizeWidth";
public static final String MOUSE_WHEEL_ZOOM = "MouseWheelZoom";
@@ -535,6 +543,10 @@ protected GUIPreferences() {
store.setDefault(CUSTOM_UNIT_HEIGHT, 400);
store.setDefault(CUSTOM_UNIT_WIDTH, 600);
+ store.setDefault(FORCE_DISPLAY_AUTO_DISPLAY_REPORT_PHASE, 2);
+ store.setDefault(FORCE_DISPLAY_AUTO_DISPLAY_NONREPORT_PHASE, 2);
+ store.setDefault(FORCE_DISPLAY_ENABLED, false);
+
store.setDefault(UNIT_DISPLAY_SIZE_HEIGHT, 500);
store.setDefault(UNIT_DISPLAY_SIZE_WIDTH, 300);
store.setDefault(UNIT_DISPLAY_NONTABBED_SIZE_HEIGHT, 900);
@@ -812,6 +824,34 @@ public int getCustomUnitWidth() {
return store.getInt(CUSTOM_UNIT_WIDTH);
}
+ public int getForceDisplayPosX() {
+ return store.getInt(FORCE_DISPLAY_POS_X);
+ }
+
+ public int getForceDisplayPosY() {
+ return store.getInt(FORCE_DISPLAY_POS_Y);
+ }
+
+ public int getForceDisplaySizeHeight() {
+ return store.getInt(FORCE_DISPLAY_SIZE_HEIGHT);
+ }
+
+ public int getForceDisplaySizeWidth() {
+ return store.getInt(FORCE_DISPLAY_SIZE_WIDTH);
+ }
+
+ public int getForceDisplayAutoDisplayReportPhase() {
+ return store.getInt(FORCE_DISPLAY_AUTO_DISPLAY_REPORT_PHASE);
+ }
+
+ public int getForceDisplayAutoDisplayNonReportPhase() {
+ return store.getInt(FORCE_DISPLAY_AUTO_DISPLAY_NONREPORT_PHASE);
+ }
+
+ public boolean getForceDisplayEnabled() {
+ return store.getBoolean(FORCE_DISPLAY_ENABLED);
+ }
+
public int getUnitDisplayPosX() {
return store.getInt(UNIT_DISPLAY_POS_X);
}
@@ -1587,6 +1627,38 @@ public void setCustomUnitWidth(int state) {
store.setValue(CUSTOM_UNIT_WIDTH, state);
}
+ public void setForceDisplayPosX(int i) {
+ store.setValue(FORCE_DISPLAY_POS_X, i);
+ }
+
+ public void setForceDisplayPosY(int i) {
+ store.setValue(FORCE_DISPLAY_POS_Y, i);
+ }
+
+ public void setForceDisplaySizeHeight(int i) {
+ store.setValue(FORCE_DISPLAY_SIZE_HEIGHT, i);
+ }
+
+ public void setForceDisplaySizeWidth(int i) {
+ store.setValue(FORCE_DISPLAY_SIZE_WIDTH, i);
+ }
+
+ public void setForceDisplayAutoDisplayReportPhase(int i) {
+ store.setValue(FORCE_DISPLAY_AUTO_DISPLAY_REPORT_PHASE, i);
+ }
+
+ public void setForceDisplayAutoDisplayNonReportPhase(int i) {
+ store.setValue(FORCE_DISPLAY_AUTO_DISPLAY_NONREPORT_PHASE, i);
+ }
+
+ public void toggleForceDisplay() {
+ store.setValue(FORCE_DISPLAY_ENABLED, !getBoolean(FORCE_DISPLAY_ENABLED));
+ }
+
+ public void setForceDisplayEnabled(boolean b) {
+ store.setValue(FORCE_DISPLAY_ENABLED, b);
+ }
+
public void setUnitDisplayPosX(int i) {
store.setValue(UNIT_DISPLAY_POS_X, i);
}
diff --git a/megamek/src/megamek/client/ui/swing/ReportDisplay.java b/megamek/src/megamek/client/ui/swing/ReportDisplay.java
index d2dda549a69..fe40d90282d 100644
--- a/megamek/src/megamek/client/ui/swing/ReportDisplay.java
+++ b/megamek/src/megamek/client/ui/swing/ReportDisplay.java
@@ -83,7 +83,7 @@ public String getHotKeyDesc() {
private Map buttons;
private boolean rerolled; // have we rerolled an init?
- private static final String RD_REPORTDIPLAY = "ReportDisplay.";
+ private static final String RD_REPORTDISPLAY = "ReportDisplay.";
private static final String RD_TOOLTIP = ".tooltip";
/**
@@ -116,7 +116,7 @@ public ReportDisplay(ClientGUI clientgui) {
protected void setButtons() {
buttons = new HashMap<>((int) (ReportCommand.values().length * 1.25 + 0.5));
for (ReportCommand cmd : ReportCommand.values()) {
- buttons.put(cmd, createButton(cmd.getCmd(), RD_REPORTDIPLAY));
+ buttons.put(cmd, createButton(cmd.getCmd(), RD_REPORTDISPLAY));
}
numButtonGroups = (int) Math.ceil((buttons.size() + 0.0) / buttonsPerGroup);
}
@@ -124,7 +124,7 @@ protected void setButtons() {
@Override
protected void setButtonsTooltips() {
for (ReportCommand cmd : ReportCommand.values()) {
- String tt = createToolTip(cmd.getCmd(), RD_REPORTDIPLAY, cmd.getHotKeyDesc());
+ String tt = createToolTip(cmd.getCmd(), RD_REPORTDISPLAY, cmd.getHotKeyDesc());
buttons.get(cmd).setToolTipText(tt);
}
}
diff --git a/megamek/src/megamek/client/ui/swing/forceDisplay/ForceDisplayDialog.java b/megamek/src/megamek/client/ui/swing/forceDisplay/ForceDisplayDialog.java
new file mode 100644
index 00000000000..cde7dbded05
--- /dev/null
+++ b/megamek/src/megamek/client/ui/swing/forceDisplay/ForceDisplayDialog.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2023 - 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.forceDisplay;
+
+import megamek.client.ui.Messages;
+import megamek.client.ui.swing.ClientGUI;
+import megamek.client.ui.swing.GUIPreferences;
+import megamek.client.ui.swing.util.UIUtil;
+
+import javax.swing.*;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+
+public class ForceDisplayDialog extends JDialog {
+ //region Variable Declarations
+ private final ClientGUI clientGUI;
+ private static final GUIPreferences GUIP = GUIPreferences.getInstance();
+ //endregion Variable Declarations
+
+ //region Constructors
+ public ForceDisplayDialog(final JFrame frame, final ClientGUI clientGUI) {
+ super(frame, "", false);
+ this.setTitle(Messages.getString("ClientGUI.ForceDisplay"));
+
+ this.clientGUI = clientGUI;
+
+ this.setLocation(GUIP.getForceDisplayPosX(), GUIP.getForceDisplayPosY());
+ this.setSize(GUIP.getForceDisplaySizeWidth(), GUIP.getForceDisplaySizeHeight());
+
+ UIUtil.updateWindowBounds(this);
+ this.setResizable(true);
+
+ addWindowListener(new WindowAdapter() {
+ @Override
+ public void windowClosing(WindowEvent evt) {
+ GUIP.setUnitDisplayEnabled(false);
+ }
+ });
+ }
+ //endregion Constructors
+
+ public void saveSettings() {
+ if ((getSize().width * getSize().height) > 0) {
+ GUIP.setForceDisplayPosX(getLocation().x);
+ GUIP.setForceDisplayPosY(getLocation().y);
+ GUIP.setForceDisplaySizeWidth(getSize().width);
+ GUIP.setForceDisplaySizeHeight(getSize().height);
+ }
+ }
+
+ @Override
+ protected void processWindowEvent(WindowEvent e) {
+ super.processWindowEvent(e);
+ if ((e.getID() == WindowEvent.WINDOW_DEACTIVATED) || (e.getID() == WindowEvent.WINDOW_CLOSING)) {
+ saveSettings();
+ }
+ }
+
+ /**
+ * In addition to the default Dialog processKeyEvent, this method
+ * dispatches a KeyEvent to the client gui.
+ * This enables all the gui hotkeys.
+ */
+ @Override
+ protected void processKeyEvent(KeyEvent evt) {
+ evt.setSource(clientGUI);
+ clientGUI.getMenuBar().dispatchEvent(evt);
+ // Make the source be the ClientGUI and not the dialog
+ // This prevents a ClassCastException in ToolTipManager
+ clientGUI.getCurrentPanel().dispatchEvent(evt);
+ if (!evt.isConsumed()) {
+ super.processKeyEvent(evt);
+ }
+ }
+}
diff --git a/megamek/src/megamek/client/ui/swing/forceDisplay/ForceDisplayMekCellFormatter.java b/megamek/src/megamek/client/ui/swing/forceDisplay/ForceDisplayMekCellFormatter.java
new file mode 100644
index 00000000000..f698420056d
--- /dev/null
+++ b/megamek/src/megamek/client/ui/swing/forceDisplay/ForceDisplayMekCellFormatter.java
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2023 - 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.forceDisplay;
+
+import megamek.MegaMek;
+import megamek.client.Client;
+import megamek.client.ui.Messages;
+import megamek.client.ui.swing.ClientGUI;
+import megamek.client.ui.swing.GUIPreferences;
+import megamek.client.ui.swing.tooltip.UnitToolTip;
+import megamek.client.ui.swing.util.PlayerColour;
+import megamek.common.*;
+import megamek.common.force.Force;
+import megamek.common.options.GameOptions;
+import megamek.common.options.OptionsConstants;
+import megamek.common.util.CollectionUtil;
+
+import java.awt.*;
+import java.text.MessageFormat;
+import java.text.NumberFormat;
+import java.util.List;
+
+import static megamek.client.ui.swing.lobby.MekTableModel.DOT_SPACER;
+import static megamek.client.ui.swing.util.UIUtil.*;
+
+class ForceDisplayMekCellFormatter {
+
+ private static final GUIPreferences GUIP = GUIPreferences.getInstance();
+
+ private ForceDisplayMekCellFormatter() {
+ }
+
+ /**
+ * Creates and returns the display content of the C3-MekTree cell for the given entity and
+ * for the compact display mode. Assumes that no enemy or blind-drop-hidden units are provided.
+ */
+ static String formatUnitCompact(Entity entity, ClientGUI clientGUI) {
+ Client client = clientGUI.getClient();
+ Game game = client.getGame();
+ GameOptions options = game.getOptions();
+ Player localPlayer = client.getLocalPlayer();
+ Player owner = entity.getOwner();
+
+ if (entity.isSensorReturn(localPlayer)) {
+ String value = " ";
+ String uType = "";
+
+ if (entity instanceof Infantry) {
+ uType = Messages.getString("ChatLounge.0");
+ } else if (entity instanceof Protomech) {
+ uType = Messages.getString("ChatLounge.1");
+ } else if (entity instanceof GunEmplacement) {
+ uType = Messages.getString("ChatLounge.2");
+ } else if (entity.isSupportVehicle()) {
+ uType = entity.getWeightClassName();
+ } else if (entity.isFighter()) {
+ uType = entity.getWeightClassName() + Messages.getString("ChatLounge.4");
+ } else if (entity instanceof Mech) {
+ uType = entity.getWeightClassName() + Messages.getString("ChatLounge.3");
+ } else if (entity instanceof Tank) {
+ uType = entity.getWeightClassName() + Messages.getString("ChatLounge.6");
+ } else {
+ uType = entity.getWeightClassName();
+ }
+
+ uType = DOT_SPACER + uType + DOT_SPACER;
+ value += guiScaledFontHTML() + uType + "";;
+ return UnitToolTip.wrapWithHTML(value);
+ } else if (!entity.isVisibleToEnemy()) {
+ return "";
+ }
+
+ StringBuilder result = new StringBuilder(" " + guiScaledFontHTML());
+ boolean isCarried = entity.getTransportId() != Entity.NONE;
+
+ Color color = GUIP.getEnemyUnitColor();
+ if (owner.getId() == localPlayer.getId()) {
+ color = GUIP.getMyUnitColor();
+ } else if (!localPlayer.isEnemyOf(owner)) {
+ color = GUIP.getAllyUnitColor();
+ }
+
+ if (entity.getForceId() == Force.NO_FORCE) {
+ result.append(guiScaledFontHTML(color) + "\u25AD" + "");
+ }
+
+ String id = MessageFormat.format("[{0}] ", entity.getId());
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor()) + id + "");
+
+ // Done
+ if (!game.getPhase().isReport()) {
+ String done = "";
+ if (!entity.isDone()) {
+ done = "\u2610 ";
+ } else {
+ done = "\u2611 ";
+ }
+ result.append(guiScaledFontHTML(color) + done + "");
+ }
+
+ // Unit name
+ // Gray out if the unit is a fighter in a squadron
+ if (entity.isPartOfFighterSquadron()) {
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor()) + entity.getShortNameRaw() + "");
+ } else {
+ result.append(entity.getShortNameRaw());
+ }
+
+ // Pilot
+ Crew pilot = entity.getCrew();
+ result.append(guiScaledFontHTML());
+ result.append(DOT_SPACER);
+
+ if (pilot.getSlotCount() > 1 || entity instanceof FighterSquadron) {
+ result.append("" + Messages.getString("ChatLounge.multipleCrew") + "");
+ } else if ((pilot.getNickname(0) != null) && !pilot.getNickname(0).isEmpty()) {
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor()) + "'");
+ result.append(pilot.getNickname(0).toUpperCase() + "'");
+ if (!pilot.getStatusDesc(0).isEmpty()) {
+ result.append(" (" + pilot.getStatusDesc(0) + ")");
+ }
+ } else {
+ result.append(pilot.getDesc(0));
+ }
+
+ final boolean rpgSkills = options.booleanOption(OptionsConstants.RPG_RPG_GUNNERY);
+ result.append(" (" + pilot.getSkillsAsString(rpgSkills) + ")");
+
+ result.append(DOT_SPACER);
+ result.append(' ' + UnitToolTip.getDamageLevelDesc(entity, true));
+
+ // Tonnage
+ result.append(DOT_SPACER);
+ NumberFormat formatter = NumberFormat.getNumberInstance(MegaMek.getMMOptions().getLocale());
+ String tonnage = formatter.format(entity.getWeight());
+ tonnage += Messages.getString("ChatLounge.Tons");
+ result.append(guiScaledFontHTML() + tonnage + "");
+
+ // Alpha Strike Unit Role
+ if (!entity.isUnitGroup()) {
+ result.append(DOT_SPACER);
+ result.append(entity.getRole().toString());
+ }
+
+ // Controls the separator dot character
+ boolean firstEntry = true;
+
+ if (pilot.countOptions() > 0) {
+ firstEntry = dotSpacerOnlyFirst(result, firstEntry);
+ String quirks = Messages.getString("ChatLounge.abilities");
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipQuirkColor()) + quirks + "");
+ }
+
+ // ECM
+ if (entity.hasActiveECM()) {
+ firstEntry = dotSpacerOnlyFirst(result, firstEntry);
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor(), 0.2f) + ECM_SIGN + "");
+ }
+
+ // Quirk Count
+ int quirkCount = entity.countQuirks() + entity.countWeaponQuirks();
+ if (quirkCount > 0) {
+ firstEntry = dotSpacerOnlyFirst(result, firstEntry);
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipQuirkColor(), 0.2f) + QUIRKS_SIGN + "");
+ }
+
+ // C3 ...
+ if (entity.hasC3i() || entity.hasNavalC3()) {
+ firstEntry = dotSpacerOnlyFirst(result, firstEntry);
+ String msg_c3i = Messages.getString("ChatLounge.C3i");
+ String msg_nc3 = Messages.getString("ChatLounge.NC3");
+
+ String c3Name = entity.hasC3i() ? msg_c3i : msg_nc3;
+ if (entity.calculateFreeC3Nodes() >= 5) {
+ c3Name += UNCONNECTED_SIGN;
+ } else {
+ c3Name += CONNECTED_SIGN + entity.getC3NetId();
+ }
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor()) + c3Name + "");
+ }
+
+ if (entity.hasC3()) {
+ String msg_c3sabrv = Messages.getString("ChatLounge.C3SAbrv");
+ String msg_c3m = Messages.getString("ChatLounge.C3M");
+ String msg_c3mcc = Messages.getString("ChatLounge.C3MCC");
+ String c3 = "";
+ firstEntry = dotSpacerOnlyFirst(result, firstEntry);
+
+ if (entity.getC3Master() == null) {
+ if (entity.hasC3S()) {
+ c3 = msg_c3sabrv + UNCONNECTED_SIGN;
+ }
+ if (entity.hasC3M()) {
+ c3 = msg_c3m;
+ }
+ } else if (entity.C3MasterIs(entity)) {
+ result.append(msg_c3mcc);
+ } else {
+ if (entity.hasC3S()) {
+ c3 = msg_c3sabrv + CONNECTED_SIGN;
+ } else {
+ c3 = msg_c3m + CONNECTED_SIGN;
+ }
+
+ c3 += entity.getC3Master().getChassis();
+ }
+
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor()) + c3 + "");
+ }
+
+ // Loaded onto another unit
+ if (isCarried) {
+ Entity loader = entity.getGame().getEntity(entity.getTransportId());
+ result.append(DOT_SPACER);
+ String carried = "(" + loader.getChassis() + " [" + entity.getTransportId() + "])";
+ carried = "" + carried + "";
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor()) + carried + "");
+ }
+
+ if (entity.countPartialRepairs() > 0) {
+ result.append(DOT_SPACER);
+ result.append(guiScaledFontHTML(GUIP.getWarningColor()) + "Partial Repairs" + "");
+ }
+
+ // Offboard deployment
+ if (entity.isOffBoard()) {
+ result.append(DOT_SPACER);
+ String msg_offboard = Messages.getString("ChatLounge.compact.deploysOffBoard");
+ msg_offboard = "" + msg_offboard + "";
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor()) + msg_offboard + "");
+ } else if (!entity.isDeployed()) {
+ result.append(DOT_SPACER);
+ String msg_deploy = Messages.getString("ChatLounge.compact.deployRound", entity.getDeployRound());
+ String msg_zone = "";
+ if (entity.getStartingPos(false) != Board.START_NONE) {
+ msg_zone = Messages.getString("ChatLounge.compact.deployZone",
+ IStartingPositions.START_LOCATION_NAMES[entity.getStartingPos(false)]);
+ }
+ msg_deploy = "" + msg_deploy + msg_zone + "";
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor()) + msg_deploy + "");
+ }
+
+ // Starting values for Altitude / Velocity / Elevation
+ if (!isCarried) {
+ if (entity.isAero()) {
+ IAero aero = (IAero) entity;
+ result.append(DOT_SPACER);
+ String msg_vel = Messages.getString("ChatLounge.compact.velocity") + ": ";
+ msg_vel += aero.getCurrentVelocity();
+ String msg_alt = "";
+ String msg_fuel = "";
+ if (!game.getBoard().inSpace()) {
+ msg_alt = ", " + Messages.getString("ChatLounge.compact.altitude") + ": ";
+ msg_alt += aero.getAltitude();
+ }
+ if (options.booleanOption(OptionsConstants.ADVAERORULES_FUEL_CONSUMPTION)) {
+ msg_fuel = ", " + Messages.getString("ChatLounge.compact.fuel") + ": ";
+ msg_fuel += aero.getCurrentFuel();
+ }
+ msg_vel = "" + msg_vel + msg_alt + msg_fuel + "";
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor()) + msg_vel + "");
+ } else if (entity.getPosition() != null && ((entity.getElevation() != 0) || (entity instanceof VTOL))) {
+ result.append(DOT_SPACER);
+ String msg_ele = Messages.getString("ChatLounge.compact.elevation") + ": ";
+ msg_ele += entity.getElevation();
+ msg_ele = "" + msg_ele + ";";
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor()) + msg_ele + "");
+ }
+ }
+
+ // Owner
+ if (!localPlayer.equals(owner)) {
+ result.append(DOT_SPACER);
+ String player = entity.getOwner().getName() + " \u2691 ";
+ result.append(guiScaledFontHTML(color) + player + "");
+ }
+
+ return UnitToolTip.wrapWithHTML(result.toString());
+ }
+
+ /**
+ * Creates and returns the display content of the C3-MekTree cell for the given entity and
+ * for the compact display mode. Assumes that no enemy or blind-drop-hidden units are provided.
+ */
+ static String formatForceCompact(Force force, ClientGUI clientGUI) {
+ return formatForce(force, clientGUI, 0);
+ }
+
+ private static String formatForce(Force force, ClientGUI clientGUI, float size) {
+ Client client = clientGUI.getClient();
+ Game game = client.getGame();
+ Player localPlayer = client.getLocalPlayer();
+ int ownerId = game.getForces().getOwnerId(force);
+ Player owner = game.getPlayer(ownerId);
+
+ // Get the my / ally / enemy color
+ Color color = GUIP.getEnemyUnitColor();
+ if (ownerId == localPlayer.getId()) {
+ color = GUIP.getMyUnitColor();
+ } else if (!localPlayer.isEnemyOf(owner)) {
+ color = GUIP.getAllyUnitColor();
+ }
+
+ StringBuilder result = new StringBuilder("");
+
+ // A top-level / subforce special char
+ String fLevel = "";
+ if (force.isTopLevel()) {
+ fLevel = "\u2327 ";
+ } else {
+ fLevel = "\u25E5 ";
+ }
+ result.append(guiScaledFontHTML(color, size) + fLevel + "");
+
+ // Name
+ String fName = force.getName();
+ fName = "" + fName + "";
+ result.append(guiScaledFontHTML(color, size) + fName + "");
+
+ // ID
+ String id = " [" + force.getId() + "]";
+ result.append(guiScaledFontHTML(GUIP.getUnitToolTipHighlightColor(), size) + id + "");
+
+ // Display force owner
+ if ((ownerId != client.getLocalPlayerNumber()) && (owner != null)) {
+ result.append(DOT_SPACER);
+ String oName = "\u2691 " + owner.getName();
+ result.append(guiScaledFontHTML(color, size) + oName + "");
+ }
+
+ // BV
+ List fullEntities = ForceAssignable.filterToEntityList(game.getForces().getFullEntities(force));
+ result.append(DOT_SPACER);
+ int totalBv = fullEntities.stream().filter(e -> !e.isPartOfFighterSquadron()).mapToInt(Entity::calculateBattleValue).sum();
+
+ if (totalBv > 0) {
+ String msg_bvplain = Messages.getString("ChatLounge.BVplain");
+ msg_bvplain = msg_bvplain + " " + String.format("%,d", totalBv);
+ result.append(guiScaledFontHTML(color, size) + msg_bvplain + "");
+
+ // Unit Type
+ long unittypes = fullEntities.stream().map(e -> Entity.getEntityMajorTypeName(e.getEntityType())).distinct().count();
+ result.append(DOT_SPACER);
+
+ if (unittypes > 1) {
+ String msg_mixed = Messages.getString("ChatLounge.Mixed");
+ result.append(guiScaledFontHTML(color, size) + msg_mixed + "");
+ } else if (unittypes == 1) {
+ Entity entity = CollectionUtil.anyOneElement(fullEntities);
+ String eType = UnitType.getTypeName(entity.getUnitType());
+ result.append(guiScaledFontHTML(color, size) + eType + "");
+ }
+
+ } else {
+ result.append(guiScaledFontHTML(color, size) + "Empty" + "");
+ }
+
+ return UnitToolTip.wrapWithHTML(result.toString());
+ }
+
+ static void formatSpan(StringBuilder current, Color color) {
+ current.append("");
+ }
+
+ static void formatSpan(StringBuilder current, String hexColor) {
+ current.append("");
+ }
+
+ static void fullidString(StringBuilder current, int id) {
+ formatSpan(current, uiGray());
+ current.append(" [ID: ").append(id).append("]");
+ }
+
+ static boolean dotSpacerOnlyFirst(StringBuilder current, boolean firstElement) {
+ if (firstElement) {
+ current.append(DOT_SPACER);
+ }
+ return false;
+ }
+}
diff --git a/megamek/src/megamek/client/ui/swing/forceDisplay/ForceDisplayMekTreeModel.java b/megamek/src/megamek/client/ui/swing/forceDisplay/ForceDisplayMekTreeModel.java
new file mode 100644
index 00000000000..501202dd003
--- /dev/null
+++ b/megamek/src/megamek/client/ui/swing/forceDisplay/ForceDisplayMekTreeModel.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2023 - 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.forceDisplay;
+
+import megamek.client.ui.swing.ClientGUI;
+import megamek.client.ui.swing.lobby.sorters.MekTreeTopLevelSorter;
+import megamek.common.Entity;
+import megamek.common.ForceAssignable;
+import megamek.common.force.Force;
+import megamek.common.force.Forces;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ForceDisplayMekTreeModel extends DefaultTreeModel {
+ private ClientGUI clientGUI;
+ /** A sorted list of all top-level objects: top-level forces and force-less entities. */
+ private ArrayList