) failureEffects.getModel();
+ btnRemoveFailure.setEnabled(false);
+ }
+
+ for (ObjectiveEffect effectToRemove : targetList.getSelectedValuesList()) {
+ modelToUpdate.removeElement(effectToRemove);
+ }
+ }
+
+ private void addForce() {
+ forceModel.addElement(cboForceName.getSelectedItem().toString());
+ pack();
+ }
+
+ private void removeForce() {
+ for (String forceName : forceNames.getSelectedValuesList()) {
+ forceModel.removeElement(forceName);
+ }
+ btnRemove.setEnabled(false);
+ pack();
+ }
+
+ private void addDetail(JTextField field) {
+ detailModel.addElement(field.getText());
+ }
+
+ private void removeDetails() {
+ for (String detail : lstDetails.getSelectedValuesList()) {
+ detailModel.removeElement(detail);
+ }
+ }
+
+ private void setDirectionDropdownVisibility() {
+ switch ((ObjectiveCriterion) cboObjectiveType.getSelectedItem()) {
+ case PreventReachMapEdge:
+ case ReachMapEdge:
+ cboDirection.setVisible(true);
+ break;
+ default:
+ cboDirection.setVisible(false);
+ break;
+ }
+ }
+
+ private void updateTimeLimitUI() {
+ boolean enable = !cboTimeScaling.getSelectedItem().equals(TimeLimitType.None);
+ spnTimeLimit.setEnabled(enable);
+ cboTimeLimitDirection.setEnabled(enable);
+ }
+
+ public ScenarioObjective getObjective() {
+ return objective;
+ }
+
+ private void saveObjectiveAndClose() {
+ objective.setObjectiveCriterion((ObjectiveCriterion) cboObjectiveType.getSelectedItem());
+ objective.setDescription(txtShortDescription.getText());
+ int number = (int) spnAmount.getValue();
+ if (cboCountType.getSelectedItem().equals(ObjectiveAmountType.Percentage)) {
+ objective.setPercentage(number);
+ objective.setFixedAmount(null);
+ } else {
+ objective.setFixedAmount(number);
+ }
+
+ objective.clearForces();
+ for (int i = 0; i< forceModel.getSize(); i++) {
+ objective.addForce(forceModel.getElementAt(i));
+ }
+
+
+ objective.clearSuccessEffects();
+ for (int i = 0; i< successEffectsModel.getSize(); i++) {
+ objective.addSuccessEffect(successEffectsModel.getElementAt(i));
+ }
+
+ objective.clearFailureEffects();
+ for (int i = 0; i< failureEffectsModel.getSize(); i++) {
+ objective.addFailureEffect(failureEffectsModel.getElementAt(i));
+ }
+
+ objective.clearDetails();
+ for (int i = 0; i< detailModel.getSize(); i++) {
+ objective.addDetail(detailModel.getElementAt(i));
+ }
+
+ if (cboDirection.isVisible() && cboDirection.getSelectedIndex() > 0) {
+ objective.setDestinationEdge(OffBoardDirection.getDirection(cboDirection.getSelectedIndex() - 1));
+ } else {
+ objective.setDestinationEdge(OffBoardDirection.NONE);
+ }
+
+ int timeLimit = (int) spnTimeLimit.getValue();
+ objective.setTimeLimitType((TimeLimitType) cboTimeScaling.getSelectedItem());
+ if (spnTimeLimit.isEnabled()) {
+ if (objective.getTimeLimitType() == TimeLimitType.ScaledToPrimaryUnitCount) {
+ objective.setTimeLimitScaleFactor(timeLimit);
+ } else {
+ objective.setTimeLimit(timeLimit);
+ }
+ }
+ if (cboTimeLimitDirection.isEnabled()) {
+ objective.setTimeLimitAtMost(cboTimeLimitDirection.getSelectedIndex() == 0);
+ }
+
+ setVisible(false);
+ }
+}
diff --git a/MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java b/MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java
new file mode 100644
index 0000000000..e43e59d121
--- /dev/null
+++ b/MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ 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.
+ *
+ * MekHQ 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 MekHQ. If not, see .
+ */
+package mekhq.gui.dialog;
+
+import megamek.client.ui.GBC;
+import megamek.client.ui.swing.util.UIUtil;
+import megamek.client.ui.swing.util.UIUtil.TipButton;
+import megamek.common.IStartingPositions;
+import megamek.common.Player;
+import mekhq.MekHQ;
+
+import javax.swing.*;
+import javax.swing.text.DefaultFormatterFactory;
+import javax.swing.text.NumberFormatter;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.text.NumberFormat;
+import java.util.ResourceBundle;
+
+import static megamek.client.ui.swing.util.UIUtil.*;
+import static megamek.client.ui.swing.util.UIUtil.guiScaledFontHTML;
+
+public class EditDeploymentDialog extends JDialog {
+
+ Player player;
+
+ private int currentStartPos;
+
+ private final JPanel panStartButtons = new JPanel();
+ private final TipButton[] butStartPos = new TipButton[11];
+ private final NumberFormatter numFormatter = new NumberFormatter(NumberFormat.getIntegerInstance());
+ private final DefaultFormatterFactory formatterFactory = new DefaultFormatterFactory(numFormatter);
+ private final JFormattedTextField txtOffset = new JFormattedTextField(formatterFactory, 0);
+ private final JFormattedTextField txtWidth = new JFormattedTextField(formatterFactory, 3);
+ private JSpinner spinStartingAnyNWx;
+ private JSpinner spinStartingAnyNWy;
+ private JSpinner spinStartingAnySEx;
+ private JSpinner spinStartingAnySEy;
+
+ public EditDeploymentDialog(JFrame parent, boolean modal, Player player) {
+ super(parent, modal);
+ this.player = player;
+ currentStartPos = player.getStartingPos();
+ initComponents();
+ setLocationRelativeTo(parent);
+ pack();
+ }
+
+ private void initComponents() {
+
+ final ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.EditDeploymentDialog",
+ MekHQ.getMHQOptions().getLocale());
+
+ panStartButtons.setAlignmentX(Component.LEFT_ALIGNMENT);
+ for (int i = 0; i < 11; i++) {
+ butStartPos[i] = new TipButton("");
+ butStartPos[i].addActionListener(startListener);
+ }
+ panStartButtons.setLayout(new GridLayout(4, 3));
+ panStartButtons.add(butStartPos[1]);
+ panStartButtons.add(butStartPos[2]);
+ panStartButtons.add(butStartPos[3]);
+ panStartButtons.add(butStartPos[8]);
+ panStartButtons.add(butStartPos[10]);
+ panStartButtons.add(butStartPos[4]);
+ panStartButtons.add(butStartPos[7]);
+ panStartButtons.add(butStartPos[6]);
+ panStartButtons.add(butStartPos[5]);
+ panStartButtons.add(butStartPos[0]);
+ panStartButtons.add(butStartPos[9]);
+ updateStartGrid();
+
+ SpinnerNumberModel mStartingAnyNWx = new SpinnerNumberModel(player.getStartingAnyNWx()+1, 0,
+ null, 1);
+ spinStartingAnyNWx = new JSpinner(mStartingAnyNWx);
+ SpinnerNumberModel mStartingAnyNWy = new SpinnerNumberModel(player.getStartingAnyNWy()+1, 0,
+ null, 1);
+ spinStartingAnyNWy = new JSpinner(mStartingAnyNWy);
+ SpinnerNumberModel mStartingAnySEx = new SpinnerNumberModel(player.getStartingAnySEx()+1, 0,
+ null, 1);
+ spinStartingAnySEx = new JSpinner(mStartingAnySEx);
+ SpinnerNumberModel mStartingAnySEy = new SpinnerNumberModel(player.getStartingAnySEy()+1, 0,
+ null, 1);
+ spinStartingAnySEy = new JSpinner(mStartingAnySEy);
+
+ GridBagLayout gbl = new GridBagLayout();
+ JPanel main = new JPanel(gbl);
+
+ JLabel lblOffset = new JLabel(resourceMap.getString("labDeploymentOffset.text"));
+ lblOffset.setToolTipText(resourceMap.getString("labDeploymentOffset.tip"));
+ JLabel lblWidth = new JLabel(resourceMap.getString("labDeploymentWidth.text"));
+ lblWidth.setToolTipText(resourceMap.getString("labDeploymentWidth.tip"));
+
+ txtOffset.setColumns(4);
+ txtWidth.setColumns(4);
+ txtWidth.setText(Integer.toString(player.getStartWidth()));
+ txtOffset.setText(Integer.toString(player.getStartOffset()));
+
+ main.add(lblOffset, GBC.std());
+ main.add(txtOffset, GBC.eol());
+ main.add(lblWidth, GBC.std());
+ main.add(txtWidth, GBC.eol());
+
+ main.add(new JLabel(resourceMap.getString("labDeploymentAnyNW.text")), GBC.std());
+ main.add(spinStartingAnyNWx, GBC.std());
+ main.add(spinStartingAnyNWy, GBC.eol());
+ main.add(new JLabel(resourceMap.getString("labDeploymentAnySE.text")), GBC.std());
+ main.add(spinStartingAnySEx, GBC.std());
+ main.add(spinStartingAnySEy, GBC.eol());
+
+ getContentPane().setLayout(new BorderLayout());
+ getContentPane().add(main, BorderLayout.CENTER);
+ getContentPane().add(panStartButtons, BorderLayout.PAGE_START);
+
+ JPanel panButtons = new JPanel(new FlowLayout());
+ JButton btnOK = new JButton(resourceMap.getString("btnOK.text"));
+ btnOK.addActionListener(this::done);
+ JButton btnCancel = new JButton(resourceMap.getString("btnCancel.text"));
+ btnCancel.addActionListener(this::cancel);
+ panButtons.add(btnOK);
+ panButtons.add(btnCancel);
+ getContentPane().add(panButtons, BorderLayout.PAGE_END);
+
+ }
+
+ private void updateStartGrid() {
+ StringBuilder[] butText = new StringBuilder[11];
+ StringBuilder[] butTT = new StringBuilder[11];
+
+ for (int i = 0; i < 11; i++) {
+ butText[i] = new StringBuilder();
+ butTT[i] = new StringBuilder();
+ }
+
+ for (int i = 0; i < 11; i++) {
+ butText[i].append("");
+ //butTT[i].append(Messages.getString("PlayerSettingsDialog.invalidStartPosTT"));
+ butText[i].append(IStartingPositions.START_LOCATION_NAMES[i]).append("
");
+ }
+
+ butText[currentStartPos].append(guiScaledFontHTML(uiGreen()));
+ butText[currentStartPos].append("\u2B24");
+
+ for (int i = 0; i < 11; i++) {
+ butStartPos[i].setText(butText[i].toString());
+ if (butTT[i].length() > 0) {
+ butStartPos[i].setToolTipText(butTT[i].toString());
+ }
+ }
+ }
+
+ ActionListener startListener = new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ // Deployment buttons
+ for (int i = 0; i < 11; i++) {
+ if (butStartPos[i].equals(e.getSource())) {
+ currentStartPos = i;
+ updateStartGrid();
+ }
+ }
+ }
+ };
+
+ private void done(ActionEvent evt) {
+ player.setStartingPos(currentStartPos);
+ player.setStartWidth(parseField(txtWidth));
+ player.setStartOffset(parseField(txtOffset));
+ player.setStartingAnyNWx(Math.min((Integer) spinStartingAnyNWx.getValue(), (Integer) spinStartingAnySEx.getValue()) - 1);
+ player.setStartingAnyNWy(Math.min((Integer) spinStartingAnyNWy.getValue(), (Integer) spinStartingAnySEy.getValue()) - 1);
+ player.setStartingAnySEx(Math.max((Integer) spinStartingAnyNWx.getValue(), (Integer) spinStartingAnySEx.getValue()) - 1);
+ player.setStartingAnySEy(Math.max((Integer) spinStartingAnyNWy.getValue(), (Integer) spinStartingAnySEy.getValue()) - 1);
+ this.setVisible(false);
+ }
+
+ private void cancel(ActionEvent evt) {
+ this.setVisible(false);
+ }
+
+ /**
+ * Parse the given field and return the integer it contains or 0, if
+ * the field cannot be parsed.
+ */
+ private int parseField(JTextField field) {
+ try {
+ return Integer.parseInt(field.getText());
+ } catch (NumberFormatException ex) {
+ return 0;
+ }
+ }
+
+}
diff --git a/MekHQ/src/mekhq/gui/dialog/EditMapSettingsDialog.java b/MekHQ/src/mekhq/gui/dialog/EditMapSettingsDialog.java
new file mode 100644
index 0000000000..f64437b443
--- /dev/null
+++ b/MekHQ/src/mekhq/gui/dialog/EditMapSettingsDialog.java
@@ -0,0 +1,473 @@
+/*
+ * 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 mekhq.gui.dialog;
+
+import megamek.client.ui.swing.lobby.LobbyUtility;
+import megamek.client.ui.swing.minimap.Minimap;
+import megamek.common.Board;
+import megamek.common.BoardDimensions;
+import megamek.common.Configuration;
+import megamek.common.MapSettings;
+import megamek.common.util.fileUtils.MegaMekFile;
+import megamek.server.GameManager;
+import megamek.server.ServerBoardHelper;
+import mekhq.MekHQ;
+import mekhq.campaign.mission.Scenario;
+import org.apache.logging.log4j.LogManager;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class EditMapSettingsDialog extends JDialog {
+
+ private int mapSizeX;
+ private int mapSizeY;
+ private String map;
+ private boolean usingFixedMap;
+ private int boardType;
+
+ private JCheckBox checkFixed;
+ private JComboBox comboBoardType;
+ private JComboBox comboMapSize;
+ private JSpinner spnMapX;
+ private JSpinner spnMapY;
+ private JScrollPane scrChooseMap;
+ private JList listMapGenerators;
+ private JList listFixedMaps;
+ DefaultListModel generatorModel = new DefaultListModel<>();
+ DefaultListModel fixedMapModel = new DefaultListModel<>();
+
+ JPanel panSizeRandom;
+ JPanel panSizeFixed;
+
+ private Map mapIcons = new HashMap<>();
+ private Map baseImages = new HashMap<>();
+
+ private ImageLoader loader;
+
+
+ public EditMapSettingsDialog(JFrame parent, boolean modal, int boardType, boolean usingFixedMap, String map,
+ int mapSizeX, int mapSizeY) {
+
+ super(parent, modal);
+ this.boardType = boardType;
+ this.usingFixedMap = usingFixedMap;
+ this.map = map;
+ this.mapSizeX = mapSizeX;
+ this.mapSizeY = mapSizeY;
+ loader = new ImageLoader();
+ loader.execute();
+
+ initComponents();
+ setLocationRelativeTo(parent);
+ pack();
+ }
+
+ public int getBoardType() {
+ return boardType;
+ }
+
+ public boolean getUsingFixedMap() {
+ return usingFixedMap;
+ }
+
+ public String getMap() {
+ return map;
+ }
+
+ public int getMapSizeX() {
+ return mapSizeX;
+ }
+
+ public int getMapSizeY() {
+ return mapSizeY;
+ }
+
+ private void initComponents() {
+ final ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.EditMapSettingsDialog",
+ MekHQ.getMHQOptions().getLocale());
+ setTitle(resourceMap.getString("dialog.title"));
+
+ getContentPane().setLayout(new BorderLayout());
+ JPanel panMain = new JPanel(new GridBagLayout());
+ panSizeRandom = new JPanel(new GridBagLayout());
+ panSizeFixed = new JPanel(new BorderLayout());
+ JPanel panButtons = new JPanel(new FlowLayout());
+
+ scrChooseMap = new JScrollPane();
+ scrChooseMap.setMinimumSize(new Dimension(600, 800));
+ scrChooseMap.setPreferredSize(new Dimension(600, 800));
+
+
+ checkFixed = new JCheckBox(resourceMap.getString("checkFixed.text"));
+ checkFixed.setSelected(usingFixedMap);
+ checkFixed.addActionListener(evt -> changeMapType());
+
+ spnMapX = new JSpinner(new SpinnerNumberModel(mapSizeX, 0, null, 1));
+ spnMapY = new JSpinner(new SpinnerNumberModel(mapSizeY, 0, null, 1));
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.weightx = 0.0;
+ gbc.weighty = 0.0;
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.fill = GridBagConstraints.NONE;
+ gbc.insets = new Insets(5, 5, 5, 5);
+ panSizeRandom.add(spnMapX, gbc);
+ gbc.gridx++;
+ panSizeRandom.add(new JLabel("x"));
+ gbc.gridx++;
+ gbc.weightx = 1.0;
+ panSizeRandom.add(spnMapY);
+
+ comboMapSize = new JComboBox<>();
+ for (BoardDimensions size : GameManager.getBoardSizes()) {
+ comboMapSize.addItem(size);
+ }
+ if (mapSizeX > 0 & mapSizeY > 0) {
+ comboMapSize.setSelectedItem(new BoardDimensions(mapSizeX, mapSizeY));
+ } else {
+ // if no board size yet set, use the default
+ comboMapSize.setSelectedItem(new BoardDimensions(16, 17));
+ }
+ comboMapSize.addActionListener(evt -> refreshBoardList());
+ panSizeFixed.add(comboMapSize, BorderLayout.CENTER);
+
+ listMapGenerators = new JList<>(generatorModel);
+ listMapGenerators.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ generatorModel.addElement(resourceMap.getString("listMapGenerators.none"));
+ File dir = new File("data/mapgen/");
+ File[] directoryListing = dir.listFiles();
+ ArrayList generators = new ArrayList<>();
+ if (directoryListing != null) {
+ for (File child : directoryListing) {
+ if (child.isFile()) {
+ String s = child.getName().replace(".xml", "");
+ generators.add(s);
+ }
+ }
+ }
+ Collections.sort(generators);
+ generatorModel.addAll(generators);
+
+ listFixedMaps = new JList<>(fixedMapModel);
+ listFixedMaps.setCellRenderer(new BoardNameRenderer());
+ listFixedMaps.setLayoutOrientation(JList.HORIZONTAL_WRAP);
+ listFixedMaps.setVisibleRowCount(-1);
+ listFixedMaps.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ refreshBoardList();
+
+ if (usingFixedMap) {
+ listFixedMaps.setSelectedValue(map, true);
+ scrChooseMap.setViewportView(listFixedMaps);
+ } else {
+ listMapGenerators.setSelectedValue(map, true);
+ scrChooseMap.setViewportView(listMapGenerators);
+ }
+
+ comboBoardType = new JComboBox<>();
+ for (int i = Scenario.T_GROUND; i <= Scenario.T_SPACE; i++) {
+ comboBoardType.addItem(Scenario.getBoardTypeName(i));
+ }
+ comboBoardType.addActionListener(evt -> changeBoardType());
+ comboBoardType.setSelectedIndex(boardType);
+
+ gbc = new GridBagConstraints();
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.weightx = 0.0;
+ gbc.weighty = 0.0;
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.fill = GridBagConstraints.NONE;
+ gbc.insets = new Insets(5, 5, 5, 5);
+ panMain.add(new JLabel(resourceMap.getString("lblBoardType.text")), gbc);
+ gbc.weightx = 1.0;
+ gbc.gridx++;
+ panMain.add(comboBoardType, gbc);
+
+ gbc.gridx = 0;
+ gbc.gridy++;
+ gbc.weightx = 0.0;
+ panMain.add(new JLabel(resourceMap.getString("lblMapSize.text")), gbc);
+ gbc.gridx++;
+ gbc.weightx = 1.0;
+ panMain.add(panSizeRandom, gbc);
+ panMain.add(panSizeFixed, gbc);
+ if (usingFixedMap) {
+ panSizeRandom.setVisible(false);
+ } else {
+ panSizeFixed.setVisible(false);
+ }
+
+ gbc.gridwidth = 2;
+ gbc.gridx = 0;
+ gbc.gridy++;
+ panMain.add(checkFixed, gbc);
+
+ gbc.gridy++;
+ gbc.fill = GridBagConstraints.BOTH;
+ gbc.weighty = 1.0;
+ panMain.add(scrChooseMap, gbc);
+
+ JButton btnOK = new JButton(resourceMap.getString("btnOK.text"));
+ btnOK.addActionListener(evt -> done());
+ JButton btnCancel = new JButton(resourceMap.getString("btnCancel.text"));
+ btnCancel.addActionListener(evt -> cancel());
+ panButtons.add(btnOK);
+ panButtons.add(btnCancel);
+
+ getContentPane().add(panMain, BorderLayout.CENTER);
+ getContentPane().add(panButtons, BorderLayout.PAGE_END);
+ }
+
+ private void changeBoardType() {
+ if (comboBoardType.getSelectedIndex() == Scenario.T_SPACE) {
+ checkFixed.setSelected(false);
+ checkFixed.setEnabled(false);
+ panSizeRandom.setVisible(true);
+ panSizeFixed.setVisible(false);
+ listMapGenerators.setSelectedIndex(0);
+ listMapGenerators.setEnabled(false);
+ listFixedMaps.setEnabled(false);
+ scrChooseMap.setViewportView(listMapGenerators);
+ } else {
+ checkFixed.setEnabled(true);
+ listMapGenerators.setEnabled(true);
+ listFixedMaps.setEnabled(true);
+ }
+ }
+
+ private void changeMapType() {
+ if (checkFixed.isSelected()) {
+ panSizeRandom.setVisible(false);
+ panSizeFixed.setVisible(true);
+ scrChooseMap.setViewportView(listFixedMaps);
+ } else {
+ panSizeRandom.setVisible(true);
+ panSizeFixed.setVisible(false);
+ scrChooseMap.setViewportView(listMapGenerators);
+ }
+ }
+
+ private void refreshBoardList() {
+ listFixedMaps.setFixedCellHeight(-1);
+ listFixedMaps.setFixedCellWidth(-1);
+ MapSettings mapSettings = MapSettings.getInstance();
+ BoardDimensions boardSize = (BoardDimensions) comboMapSize.getSelectedItem();
+ mapSettings.setBoardSize(boardSize.width(), boardSize.height());
+ List boards = ServerBoardHelper.scanForBoards(mapSettings);
+ fixedMapModel.removeAllElements();
+ fixedMapModel.addAll(boards);
+ listFixedMaps.clearSelection();
+ }
+
+ public void done() {
+ boardType = comboBoardType.getSelectedIndex();
+ usingFixedMap = checkFixed.isSelected();
+ if (usingFixedMap) {
+ map = listFixedMaps.getSelectedValue();
+ BoardDimensions boardSize = (BoardDimensions) comboMapSize.getSelectedItem();
+ mapSizeX = boardSize.width();
+ mapSizeY = boardSize.height();
+ } else {
+ map = listMapGenerators.getSelectedValue();
+ if (listMapGenerators.getSelectedIndex() == 0) {
+ map = null;
+ }
+ mapSizeX = (int) spnMapX.getValue();
+ mapSizeY = (int) spnMapY.getValue();
+ }
+ setVisible(false);
+ }
+
+ public void cancel() {
+ setVisible(false);
+ }
+
+ /**
+ * A modified version of the thumbnail board rendered from Megamek.ChatLounge
+ */
+ private class BoardNameRenderer extends DefaultListCellRenderer {
+
+ private Image image;
+ private ImageIcon icon;
+
+ @Override
+ public Component getListCellRendererComponent(JList> list, Object value,
+ int index, boolean isSelected, boolean cellHasFocus) {
+
+ String board = (String) value;
+ // For generated boards, add the size to have different images for different sizes
+ //if (board.startsWith(MapSettings.BOARD_GENERATED)) {
+ // board += comboMapSize.getSelectedItem();
+ //}
+
+ // If an icon is present for the current board, use it
+ icon = mapIcons.get(board);
+ if (icon != null) {
+ setIcon(icon);
+ } else {
+ // The icon is not present, see if there's a base image
+ synchronized (baseImages) {
+ image = baseImages.get(board);
+ }
+ if (image == null) {
+ // There's no base image: trigger loading it and, for now, return the base list's panel
+ // The [GENERATED] entry will always land here as well
+ loader.add(board);
+ setToolTipText(null);
+ return super.getListCellRendererComponent(list, new File(board).getName(), index, isSelected, cellHasFocus);
+ } else {
+ icon = new ImageIcon(image);
+
+ mapIcons.put(board, icon);
+ setIcon(icon);
+ }
+ }
+
+ // Found or created an icon; finish the panel
+ setText("");
+ if (listFixedMaps.isEnabled()) {
+ setToolTipText(board);
+ } else {
+ setToolTipText(null);
+ }
+
+ if (isSelected) {
+ setForeground(list.getSelectionForeground());
+ setBackground(list.getSelectionBackground());
+ } else {
+ setForeground(list.getForeground());
+ setBackground(list.getBackground());
+ }
+
+ return this;
+ }
+ }
+
+ private class ImageLoader extends SwingWorker {
+
+ private BlockingQueue boards = new LinkedBlockingQueue<>();
+
+ private synchronized void add(String name) {
+ if (!boards.contains(name)) {
+ try {
+ boards.put(name);
+ } catch (Exception e) {
+ LogManager.getLogger().error("", e);
+ }
+ }
+ }
+
+ private Image prepareImage(String boardName) {
+ MapSettings mapSettings = MapSettings.getInstance();
+ BoardDimensions boardSize = (BoardDimensions) comboMapSize.getSelectedItem();
+ mapSettings.setBoardSize(boardSize.width(), boardSize.height());
+ File boardFile = new MegaMekFile(Configuration.boardsDir(), boardName + ".board").getFile();
+ Board board;
+ StringBuffer errs = new StringBuffer();
+ if (boardFile.exists()) {
+ board = new Board();
+ try (InputStream is = new FileInputStream(boardFile)) {
+ board.load(is, errs, true);
+ } catch (IOException ex) {
+ board = Board.createEmptyBoard(mapSettings.getBoardWidth(), mapSettings.getBoardHeight());
+ }
+ } else {
+ board = Board.createEmptyBoard(mapSettings.getBoardWidth(), mapSettings.getBoardHeight());
+ }
+
+ // Determine a minimap zoom from the board size and gui scale.
+ // This is very magic numbers but currently the minimap has only fixed zoom states.
+ int largerEdge = Math.max(board.getWidth(), board.getHeight());
+ int zoom = 3;
+ if (largerEdge < 17) {
+ zoom = 4;
+ }
+ if (largerEdge > 20) {
+ zoom = 2;
+ }
+ if (largerEdge > 30) {
+ zoom = 1;
+ }
+ if (largerEdge > 40) {
+ zoom = 0;
+ }
+ if (board.getWidth() < 25) {
+ zoom = Math.max(zoom, 3);
+ }
+ float scale = 1;
+ zoom = (int) (scale*zoom);
+ if (zoom > 6) {
+ zoom = 6;
+ }
+ if (zoom < 0) {
+ zoom = 0;
+ }
+ BufferedImage bufImage = Minimap.getMinimapImage(board, zoom);
+
+ // Add the board name label and the server-side board label if necessary
+ String text = LobbyUtility.cleanBoardName(boardName, mapSettings);
+ Graphics g = bufImage.getGraphics();
+ LobbyUtility.drawMinimapLabel(text, bufImage.getWidth(), bufImage.getHeight(), g, errs.length() != 0);
+ g.dispose();
+
+ synchronized(baseImages) {
+ baseImages.put(boardName, bufImage);
+ }
+ return bufImage;
+ }
+
+
+ @Override
+ protected Void doInBackground() throws Exception {
+ Image image;
+ while (!isCancelled()) {
+ // Create thumbnails for the MapSettings boards
+ String boardName = boards.poll(1, TimeUnit.SECONDS);
+ if (boardName != null && !baseImages.containsKey(boardName)) {
+ image = prepareImage(boardName);
+ redrawMapTable(image);
+ }
+ }
+ return null;
+ }
+
+ private void redrawMapTable(Image image) {
+ if (image != null) {
+ if (listFixedMaps.getFixedCellHeight() != image.getHeight(null)
+ || listFixedMaps.getFixedCellWidth() != image.getWidth(null)) {
+ listFixedMaps.setFixedCellHeight(image.getHeight(null));
+ listFixedMaps.setFixedCellWidth(image.getWidth(null));
+ }
+ listFixedMaps.repaint();
+ }
+ }
+ }
+}
diff --git a/MekHQ/src/mekhq/gui/dialog/EditScenarioDeploymentLimitDialog.java b/MekHQ/src/mekhq/gui/dialog/EditScenarioDeploymentLimitDialog.java
new file mode 100644
index 0000000000..cc193feba4
--- /dev/null
+++ b/MekHQ/src/mekhq/gui/dialog/EditScenarioDeploymentLimitDialog.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ 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.
+ *
+ * MekHQ 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 MekHQ. If not, see .
+ */
+package mekhq.gui.dialog;
+
+import megamek.client.ui.baseComponents.MMComboBox;
+import megamek.common.UnitType;
+import mekhq.MekHQ;
+import mekhq.campaign.mission.ScenarioDeploymentLimit;
+import mekhq.campaign.mission.ScenarioDeploymentLimit.CountType;
+import mekhq.campaign.mission.ScenarioDeploymentLimit.QuantityType;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.util.ArrayList;
+import java.util.ResourceBundle;
+
+public class EditScenarioDeploymentLimitDialog extends JDialog {
+
+ private ScenarioDeploymentLimit deploymentLimit;
+ private boolean newLimit;
+
+ private JSpinner spnQuantity;
+ private MMComboBox choiceQuantityType;
+ private MMComboBox choiceCountType;
+ private JCheckBox checkAllUnits;
+ private JCheckBox[] checkAllowedUnits;
+
+ public EditScenarioDeploymentLimitDialog(JFrame parent, boolean modal, ScenarioDeploymentLimit limit) {
+ super(parent, modal);
+ if (limit == null) {
+ deploymentLimit = new ScenarioDeploymentLimit();
+ newLimit = true;
+ } else {
+ deploymentLimit = limit;
+ newLimit = false;
+ }
+ initComponents();
+ setLocationRelativeTo(parent);
+ pack();
+ }
+
+ private void initComponents() {
+ final ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.EditScenarioDeploymentLimitsDialog",
+ MekHQ.getMHQOptions().getLocale());
+ setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ setTitle(resourceMap.getString("dialog.title"));
+
+ getContentPane().setLayout(new BorderLayout());
+ JPanel panMain = new JPanel(new GridBagLayout());
+ JPanel panButtons = new JPanel(new FlowLayout());
+
+ GridBagConstraints leftGbc = new GridBagConstraints();
+ leftGbc.gridx = 0;
+ leftGbc.gridy = 0;
+ leftGbc.gridwidth = 1;
+ leftGbc.weightx = 0.0;
+ leftGbc.weighty = 0.0;
+ leftGbc.insets = new Insets(5, 5, 5, 10);
+ leftGbc.fill = GridBagConstraints.NONE;
+ leftGbc.anchor = GridBagConstraints.NORTHWEST;
+
+ GridBagConstraints rightGbc = new GridBagConstraints();
+ rightGbc.gridx = 1;
+ rightGbc.gridy = 0;
+ rightGbc.gridwidth = 1;
+ rightGbc.weightx = 1.0;
+ rightGbc.weighty = 0.0;
+ rightGbc.insets = new Insets(5, 10, 5, 5);
+ rightGbc.fill = GridBagConstraints.HORIZONTAL;
+ rightGbc.anchor = GridBagConstraints.NORTHWEST;
+
+ panMain.add(new JLabel(resourceMap.getString("lblQuantityType.text")), leftGbc);
+ choiceQuantityType = new MMComboBox<>("choiceQuantityType", QuantityType.values());
+ choiceQuantityType.setSelectedItem(deploymentLimit.getQuantityType());
+ choiceQuantityType.addActionListener(this::setQuantityModel);
+ panMain.add(choiceQuantityType, rightGbc);
+
+
+ leftGbc.gridy++;
+ panMain.add(new JLabel(resourceMap.getString("lblCountType.text")), leftGbc);
+ choiceCountType = new MMComboBox<>("choiceCountType", CountType.values());
+ choiceCountType.setSelectedItem(deploymentLimit.getCountType());
+ choiceCountType.addActionListener(this::setQuantityModel);
+ rightGbc.gridy++;
+ panMain.add(choiceCountType, rightGbc);
+
+
+ leftGbc.gridy++;
+ panMain.add(new JLabel(resourceMap.getString("lblQuantity.text")), leftGbc);
+ spnQuantity = new JSpinner();
+ spnQuantity.setValue(deploymentLimit.getQuantityLimit());
+ setQuantityModel(null);
+ rightGbc.gridy++;
+ panMain.add(spnQuantity, rightGbc);
+
+ JPanel panAllowedUnits = new JPanel(new GridLayout(UnitType.SIZE+1, 1));
+ panAllowedUnits.setBorder(BorderFactory.createCompoundBorder(
+ BorderFactory.createEmptyBorder(0, 0, 10, 0),
+ BorderFactory.createTitledBorder(resourceMap.getString("panAllowedUnits.title"))));
+ checkAllUnits = new JCheckBox(resourceMap.getString("checkAllUnits.text"));
+ checkAllUnits.setSelected(deploymentLimit.getAllowedUnitTypes().isEmpty());
+ checkAllUnits.addActionListener(this::checkAllUnits);
+ panAllowedUnits.add(checkAllUnits);
+ checkAllowedUnits = new JCheckBox [UnitType.SIZE];
+ for (int i = UnitType.MEK; i < UnitType.SIZE; i++) {
+ JCheckBox check = new JCheckBox(UnitType.getTypeName(i));
+ check.setSelected(deploymentLimit.getAllowedUnitTypes().contains(i));
+ check.setEnabled(!checkAllUnits.isSelected());
+ checkAllowedUnits[i] = check;
+ panAllowedUnits.add(check);
+ }
+
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.gridx = 2;
+ gbc.gridy = 0;
+ gbc.weightx = 0.0;
+ gbc.weighty = 1.0;
+ gbc.fill = GridBagConstraints.BOTH;
+ gbc.anchor = GridBagConstraints.NORTHWEST;
+ rightGbc.insets = new Insets(5, 5, 5, 5);
+ gbc.gridheight = 3;
+ panMain.add(panAllowedUnits, gbc);
+
+ JButton btnOk = new JButton(resourceMap.getString("btnOK.text"));
+ btnOk.addActionListener(this::complete);
+ JButton btnClose = new JButton(resourceMap.getString("btnCancel.text"));
+ btnClose.addActionListener(this::cancel);
+ panButtons.add(btnOk);
+ panButtons.add(btnClose);
+
+ getContentPane().add(panMain, BorderLayout.CENTER);
+ getContentPane().add(panButtons, BorderLayout.PAGE_END);
+ }
+
+ private void checkAllUnits(ActionEvent evt) {
+ for (JCheckBox box : checkAllowedUnits) {
+ if (checkAllUnits.isSelected()) {
+ box.setSelected(false);
+ box.setEnabled(false);
+ } else {
+ box.setEnabled(true);
+ }
+ }
+ }
+
+ private void setQuantityModel(ActionEvent evt) {
+ int currentQuantity = (int) spnQuantity.getValue();
+ if (currentQuantity < 1) {
+ currentQuantity = 1;
+ }
+ CountType currentCountType = choiceCountType.getSelectedItem();
+ QuantityType currentQuantityType = choiceQuantityType.getSelectedItem();
+ if (currentQuantityType == QuantityType.PERCENT) {
+ if (currentQuantity > 100) {
+ currentQuantity = 100;
+ }
+ spnQuantity.setModel(new SpinnerNumberModel(currentQuantity, 1, 100, 5));
+ } else {
+ if (currentCountType == CountType.UNIT) {
+ spnQuantity.setModel(new SpinnerNumberModel(currentQuantity, 1, null, 1));
+ } else {
+ spnQuantity.setModel(new SpinnerNumberModel(currentQuantity, 1, null, 500));
+ }
+ }
+ }
+
+ private void complete(ActionEvent evt) {
+ deploymentLimit.setQuantityLimit((int) spnQuantity.getValue());
+ deploymentLimit.setQuantityType(choiceQuantityType.getSelectedItem());
+ deploymentLimit.setCountType(choiceCountType.getSelectedItem());
+ ArrayList allowed = new ArrayList<>();
+ if (!checkAllUnits.isSelected()) {
+ for (int i = UnitType.MEK; i < UnitType.SIZE; i++) {
+ if (checkAllowedUnits[i].isSelected()) {
+ allowed.add(i);
+ }
+ }
+ }
+ deploymentLimit.setAllowedUnitTypes(allowed);
+ this.setVisible(false);
+ }
+
+ private void cancel(ActionEvent evt) {
+ if (newLimit) {
+ deploymentLimit = null;
+ }
+ this.setVisible(false);
+ }
+
+ public ScenarioDeploymentLimit getDeploymentLimit() {
+ return deploymentLimit;
+ }
+}
diff --git a/MekHQ/src/mekhq/gui/model/BotForceTableModel.java b/MekHQ/src/mekhq/gui/model/BotForceTableModel.java
new file mode 100644
index 0000000000..1ba43f5f5c
--- /dev/null
+++ b/MekHQ/src/mekhq/gui/model/BotForceTableModel.java
@@ -0,0 +1,192 @@
+/*
+ * BotForceTableModel.java
+ *
+ * Copyright (c) 2009 Jay Lawson (jaylawson39 at yahoo.com). All rights reserved.
+ * Copyright (c) 2020 - The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ 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.
+ *
+ * MekHQ 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 MekHQ. If not, see .
+ */
+package mekhq.gui.model;
+
+
+import mekhq.Utilities;
+import mekhq.campaign.Campaign;
+import mekhq.campaign.mission.BotForce;
+
+import javax.swing.*;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.DefaultTableCellRenderer;
+import java.awt.*;
+import java.util.List;
+
+public class BotForceTableModel extends AbstractTableModel {
+
+ //region Variable Declarations
+ protected String[] columnNames;
+ protected List data;
+ private Campaign campaign;
+
+ public final static int COL_NAME = 0;
+ public final static int COL_IFF = 1;
+ public final static int COL_FIXED = 2;
+ public final static int COL_RANDOM = 3;
+ public final static int COL_DEPLOYMENT = 4;
+ public final static int N_COL = 5;
+ //endregion Variable Declarations
+
+ public BotForceTableModel(List entries, Campaign c) {
+ data = entries;
+ this.campaign = c;
+ }
+
+ @Override
+ public int getRowCount() {
+ return data.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return N_COL;
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ switch (column) {
+ case COL_NAME:
+ return "Name";
+ case COL_IFF:
+ return "IFF";
+ case COL_FIXED:
+ return "Fixed";
+ case COL_RANDOM:
+ return "Random";
+ case COL_DEPLOYMENT:
+ return "Deployment";
+ default:
+ return "?";
+ }
+ }
+
+ @Override
+ public Object getValueAt(int row, int col) {
+ BotForce botForce;
+ if (data.isEmpty()) {
+ return "";
+ } else {
+ botForce = getBotForceAt(row);
+ }
+
+ switch (col) {
+ case COL_NAME:
+ return botForce.getName();
+ case COL_IFF:
+ return (botForce.getTeam() == 1) ? "Allied" : "Enemy (Team " + botForce.getTeam() + ")";
+ case COL_FIXED:
+ return botForce.getFixedEntityList().size() + " Units, BV: " + botForce.getFixedBV();
+ case COL_RANDOM:
+ return ((null == botForce.getBotForceRandomizer()) ? "" : botForce.getBotForceRandomizer().
+ getShortDescription());
+ case COL_DEPLOYMENT:
+ return Utilities.getDeploymentString(botForce);
+ default:
+ return "?";
+ }
+ }
+
+ @Override
+ public Class> getColumnClass(int c) {
+ return getValueAt(0, c).getClass();
+ }
+
+ public BotForce getBotForceAt(int row) {
+ return data.get(row);
+ }
+
+ public void addForce(BotForce botForce) {
+ data.add(botForce);
+ fireTableDataChanged();
+ }
+
+ public List getAllBotForces() {
+ return data;
+ }
+
+ public int getColumnWidth(int col) {
+ switch (col) {
+ case COL_NAME:
+ return 80;
+ case COL_DEPLOYMENT:
+ return 20;
+ default:
+ return 30;
+ }
+ }
+
+ public int getAlignment(int col) {
+ switch (col) {
+ case COL_NAME:
+ case COL_IFF:
+ return SwingConstants.LEFT;
+ case COL_DEPLOYMENT:
+ return SwingConstants.CENTER;
+ default:
+ return SwingConstants.RIGHT;
+ }
+ }
+
+ public String getTooltip(int row, int col) {
+ BotForce botForce;
+ if (data.isEmpty()) {
+ return "";
+ } else {
+ botForce = getBotForceAt(row);
+ }
+
+ switch (col) {
+ case COL_RANDOM:
+ return ((null == botForce.getBotForceRandomizer()) ? "" : botForce.getBotForceRandomizer().
+ getDescription(campaign));
+ default:
+ return null;
+ }
+ }
+
+ //fill table with values
+ public void setData(List botForce) {
+ data = botForce;
+ fireTableDataChanged();
+ }
+
+ public BotForceTableModel.Renderer getRenderer() {
+ return new BotForceTableModel.Renderer();
+ }
+
+ public class Renderer extends DefaultTableCellRenderer {
+ @Override
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected, boolean hasFocus,
+ int row, int column) {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ setOpaque(true);
+ int actualCol = table.convertColumnIndexToModel(column);
+ int actualRow = table.convertRowIndexToModel(row);
+ setHorizontalAlignment(getAlignment(actualCol));
+ setToolTipText(getTooltip(actualRow, actualCol));
+
+ return this;
+ }
+ }
+}
diff --git a/MekHQ/src/mekhq/gui/model/ObjectiveTableModel.java b/MekHQ/src/mekhq/gui/model/ObjectiveTableModel.java
new file mode 100644
index 0000000000..d4c6c6fef2
--- /dev/null
+++ b/MekHQ/src/mekhq/gui/model/ObjectiveTableModel.java
@@ -0,0 +1,193 @@
+/*
+ * 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 mekhq.gui.model;
+
+import mekhq.campaign.mission.ObjectiveEffect;
+import mekhq.campaign.mission.ScenarioObjective;
+import mekhq.campaign.mission.ScenarioObjective.ObjectiveAmountType;
+import mekhq.campaign.mission.ScenarioObjective.TimeLimitType;
+
+import javax.swing.*;
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.DefaultTableCellRenderer;
+import java.awt.*;
+import java.util.List;
+
+/**
+ * TableModel for displaying information about a scenario objective
+ */
+public class ObjectiveTableModel extends AbstractTableModel {
+ //region Variable Declarations
+ protected String[] columnNames;
+ protected List data;
+
+ public final static int COL_CRITERION = 0;
+ public final static int COL_AMOUNT = 1;
+ public final static int COL_TIME = 2;
+ public final static int COL_SUCCESS_EFFECT = 3;
+ public final static int COL_FAILURE_EFFECT = 4;
+ public final static int N_COL = 5;
+ //endregion Variable Declarations
+
+ public ObjectiveTableModel(List entries) {
+ data = entries;
+ }
+
+ @Override
+ public int getRowCount() {
+ return data.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return N_COL;
+ }
+
+ @Override
+ public String getColumnName(int column) {
+ switch (column) {
+ case COL_CRITERION:
+ return "Type";
+ case COL_AMOUNT:
+ return "Amount";
+ case COL_TIME:
+ return "Time limits";
+ case COL_SUCCESS_EFFECT:
+ return "On Success";
+ case COL_FAILURE_EFFECT:
+ return "On Failure";
+ default:
+ return "?";
+ }
+ }
+
+ public void addObjective(ScenarioObjective objective) {
+ data.add(objective);
+ fireTableDataChanged();
+ }
+
+ @Override
+ public Object getValueAt(int row, int col) {
+ ScenarioObjective objective;
+ if (data.isEmpty()) {
+ return "";
+ } else {
+ objective = getObjectiveAt(row);
+ }
+
+ switch (col) {
+ case COL_CRITERION:
+ return objective.getObjectiveCriterion().toString();
+ case COL_AMOUNT:
+ return objective.getAmountType().equals(ObjectiveAmountType.Percentage) ?
+ objective.getPercentage() + "%" : objective.getAmount() + " units";
+ case COL_TIME:
+ if(objective.getTimeLimitType().equals(TimeLimitType.None)) {
+ return "None";
+ }
+ String timeDirection = objective.isTimeLimitAtMost() ? "At most " : "At least ";
+ return objective.getTimeLimitType().equals(TimeLimitType.Fixed) ?
+ timeDirection + objective.getTimeLimit() + " turns" :
+ timeDirection + '(' + objective.getTimeLimitScaleFactor() + "x unit count) turns";
+ case COL_SUCCESS_EFFECT:
+ return objective.getSuccessEffects().size() + " Effect(s)";
+ case COL_FAILURE_EFFECT:
+ return objective.getFailureEffects().size() + " Effect(s)";
+ default:
+ return "?";
+ }
+ }
+
+ public ScenarioObjective getObjectiveAt(int row) {
+ return data.get(row);
+ }
+
+ public int getColumnWidth(int c) {
+ switch (c) {
+ default:
+ return 20;
+ }
+ }
+
+ public int getAlignment(int col) {
+ switch (col) {
+ default:
+ return SwingConstants.LEFT;
+ }
+ }
+
+ public String getTooltip(int row, int col) {
+ ScenarioObjective objective;
+ if (data.isEmpty()) {
+ return null;
+ } else {
+ objective = getObjectiveAt(row);
+ }
+ StringBuilder sb;
+
+ switch (col) {
+ case COL_CRITERION:
+ return "" + String.join("
", objective.getAssociatedForceNames()) +"";
+ case COL_SUCCESS_EFFECT:
+ sb = new StringBuilder();
+ sb.append("");
+ for(ObjectiveEffect effect : objective.getSuccessEffects()) {
+ sb.append(effect.toString()).append("
");
+ }
+ sb.append("");
+ return sb.toString();
+ case COL_FAILURE_EFFECT:
+ sb = new StringBuilder();
+ sb.append("");
+ for(ObjectiveEffect effect : objective.getFailureEffects()) {
+ sb.append(effect.toString()).append("
");
+ }
+ sb.append("");
+ return sb.toString();
+ default:
+ return null;
+ }
+ }
+
+ //fill table with values
+ public void setData(List objectives) {
+ data = objectives;
+ fireTableDataChanged();
+ }
+
+ public Renderer getRenderer() {
+ return new Renderer();
+ }
+
+ public class Renderer extends DefaultTableCellRenderer {
+ @Override
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected, boolean hasFocus,
+ int row, int column) {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ setOpaque(true);
+ int actualCol = table.convertColumnIndexToModel(column);
+ int actualRow = table.convertRowIndexToModel(row);
+ setHorizontalAlignment(getAlignment(actualCol));
+ setToolTipText(getTooltip(actualRow, actualCol));
+
+ return this;
+ }
+ }
+}
diff --git a/MekHQ/src/mekhq/gui/view/AtBScenarioViewPanel.java b/MekHQ/src/mekhq/gui/view/AtBScenarioViewPanel.java
index e6752f9059..19662c9272 100644
--- a/MekHQ/src/mekhq/gui/view/AtBScenarioViewPanel.java
+++ b/MekHQ/src/mekhq/gui/view/AtBScenarioViewPanel.java
@@ -28,6 +28,7 @@
import megamek.common.planetaryconditions.Atmosphere;
import megamek.common.planetaryconditions.PlanetaryConditions;
import mekhq.MekHQ;
+import mekhq.Utilities;
import mekhq.campaign.Campaign;
import mekhq.campaign.force.ForceStub;
import mekhq.campaign.force.UnitStub;
@@ -123,9 +124,9 @@ public AtBScenarioViewPanel(AtBScenario s, Campaign c, JFrame frame) {
if (s.getStatus().isCurrent()) {
s.refresh(c);
this.playerForces = new ForceStub(s.getForces(campaign), campaign);
- attachedAllyStub = s.generateEntityStub(s.getAlliesPlayer());
+ attachedAllyStub = Utilities.generateEntityStub(s.getAlliesPlayer());
for (int i = 0; i < s.getNumBots(); i++) {
- botStubs.add(s.generateBotStub(s.getBotForce(i), campaign));
+ botStubs.add(s.getBotForce(i).generateStub(campaign));
}
} else {
this.playerForces = s.getForceStub();
diff --git a/MekHQ/src/mekhq/gui/view/ScenarioViewPanel.java b/MekHQ/src/mekhq/gui/view/ScenarioViewPanel.java
index eef9caa389..48b6db6657 100644
--- a/MekHQ/src/mekhq/gui/view/ScenarioViewPanel.java
+++ b/MekHQ/src/mekhq/gui/view/ScenarioViewPanel.java
@@ -80,7 +80,7 @@ public ScenarioViewPanel(JFrame f, Campaign c, Scenario s) {
botStubs = new ArrayList<>();
if (s.getStatus().isCurrent()) {
for (int i = 0; i < s.getNumBots(); i++) {
- botStubs.add(s.generateBotStub(s.getBotForce(i), campaign));
+ botStubs.add(s.getBotForce(i).generateStub(campaign));
}
} else {
botStubs = s.getBotForcesStubs();