From 10db9fc13e66eb223a9c58f4ec8aec14178c71e6 Mon Sep 17 00:00:00 2001 From: algebro Date: Thu, 3 Oct 2024 22:21:48 -0400 Subject: [PATCH 1/7] fix a/an grammar in camops personnel market notification --- MekHQ/src/mekhq/campaign/market/PersonnelMarket.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java b/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java index 69effc659b..c44c613622 100644 --- a/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java +++ b/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java @@ -151,8 +151,12 @@ public void generatePersonnelForDay(Campaign campaign) { stringBuilder.append(':'); Person person = personnel.get(0); String expLevel = SkillType.getExperienceLevelName(person.getExperienceLevel(campaign, false)); - stringBuilder.append("
A ") - .append(" ") + if (expLevel.equals("Elite") || expLevel.equals("Ultra-Green")) { + stringBuilder.append("
An "); + } else { + stringBuilder.append("
A "); + } + stringBuilder.append(" ") .append(expLevel) .append(' ') .append(person.getPrimaryRole().toString()) From 1d299da0fec00a96b032d4270ab8175016323500 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Fri, 4 Oct 2024 01:04:24 -0500 Subject: [PATCH 2/7] Implemented Campaign Options IIC Preset Picker Replaced the existing preset picker class with `SelectPresetDialog` - the preset picker that was developed for the Campaign Options IIC project. I figured compartmentalizing would make reviewing easier. Especially as CO IIC is going to be a hefty merge. --- .../resources/mekhq/resources/GUI.properties | 3 - .../NEWCampaignOptionsDialog.properties | 11 + MekHQ/src/mekhq/campaign/CampaignPreset.java | 49 ++--- MekHQ/src/mekhq/gui/CampaignGUI.java | 13 +- .../gui/dialog/CampaignOptionsDialog.java | 8 +- .../dialog/CampaignPresetSelectionDialog.java | 79 ------- .../mekhq/gui/dialog/DataLoadingDialog.java | 67 +++--- .../mekhq/gui/panes/CampaignPresetPane.java | 104 --------- .../campaignOptions/SelectPresetDialog.java | 199 ++++++++++++++++++ 9 files changed, 284 insertions(+), 249 deletions(-) create mode 100644 MekHQ/resources/mekhq/resources/NEWCampaignOptionsDialog.properties delete mode 100644 MekHQ/src/mekhq/gui/dialog/CampaignPresetSelectionDialog.java delete mode 100644 MekHQ/src/mekhq/gui/panes/CampaignPresetPane.java create mode 100644 MekHQ/src/mekhq/gui/panes/campaignOptions/SelectPresetDialog.java diff --git a/MekHQ/resources/mekhq/resources/GUI.properties b/MekHQ/resources/mekhq/resources/GUI.properties index 11eb2a4ca0..e3dae29a9d 100644 --- a/MekHQ/resources/mekhq/resources/GUI.properties +++ b/MekHQ/resources/mekhq/resources/GUI.properties @@ -433,9 +433,6 @@ btnNewYear.toolTipText=Advance to the first day of the next year (e.g., 01-JAN-3 btnNewQuinquennial.text=New Quinquennial btnNewQuinquennial.toolTipText=Advance to the first day of the next quinquennial (e.g., 01-JAN-3025, 01-JAN-3030).
WARNING: This will take a large amount of RAM to run, and may cause MekHQ to crash if there is not enough allocated. -### CampaignPresetSelectionDialog Class -CampaignPresetSelectionDialog.title=Select Campaign Preset - ### StoryArcSelectionDialog Class StoryArcSelectionDialog.title=Select a Story Arc diff --git a/MekHQ/resources/mekhq/resources/NEWCampaignOptionsDialog.properties b/MekHQ/resources/mekhq/resources/NEWCampaignOptionsDialog.properties new file mode 100644 index 0000000000..837d87a1db --- /dev/null +++ b/MekHQ/resources/mekhq/resources/NEWCampaignOptionsDialog.properties @@ -0,0 +1,11 @@ +## SelectPresetDialog Class +# SelectPresetDialog +presetDialog.title=Select a Campaign Preset +presetDialog.description=Choose a preset to tailor your experience. Each option provides a\ + \ well-balanced challenge, suitable for both newcomers and seasoned players. +presetDialogSelect.name=Confirm +presetDialogSelect.tooltip=Confirm the currently selected preset. +presetDialogCustomize.name=Customize +presetDialogCustomize.tooltip=Select a preset, then open the campaign options menu. +presetDialogCancel.name=Cancel +presetDialogCancel.tooltip=Return to the prior screen. \ No newline at end of file diff --git a/MekHQ/src/mekhq/campaign/CampaignPreset.java b/MekHQ/src/mekhq/campaign/CampaignPreset.java index 4b996ad4e3..cf58104fc4 100644 --- a/MekHQ/src/mekhq/campaign/CampaignPreset.java +++ b/MekHQ/src/mekhq/campaign/CampaignPreset.java @@ -18,33 +18,6 @@ */ package mekhq.campaign; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.ResourceBundle; -import java.util.stream.Collectors; - -import javax.swing.JFrame; -import javax.swing.JOptionPane; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import megamek.Version; import megamek.common.annotations.Nullable; import megamek.common.options.GameOptions; @@ -64,15 +37,26 @@ import mekhq.campaign.universe.Systems; import mekhq.campaign.universe.companyGeneration.CompanyGenerationOptions; import mekhq.utilities.MHQXMLUtility; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.swing.*; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; /** * This is an object which holds a set of objects that collectively define the * initial options setup for a campaign. - * + *

* It includes both startup values, which are only used on initial startup (the * date, starting planet, and rank system), and continuous options, which can be * applied at any time (campaign options, skills, SPAs). - * + *

* It also includes a short title and description that allows one to create and * save different presets. The goal is to allow users to create and load various * different presets. @@ -162,6 +146,13 @@ public boolean isUserData() { return userData; } + /** + * @return the title of the {@link CampaignPreset} + */ + public String getTitle() { + return title; + } + public void setTitle(final String title) { this.title = title; } diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index 645e8ad867..a86193246f 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -74,6 +74,7 @@ import mekhq.gui.dialog.reportDialogs.*; import mekhq.gui.enums.MHQTabType; import mekhq.gui.model.PartsTableModel; +import mekhq.gui.panes.campaignOptions.SelectPresetDialog; import mekhq.io.FileType; import mekhq.utilities.MHQXMLUtility; import org.w3c.dom.Document; @@ -95,6 +96,8 @@ import java.util.stream.IntStream; import java.util.zip.GZIPOutputStream; +import static mekhq.gui.panes.campaignOptions.SelectPresetDialog.PRESET_SELECTION_CANCELLED; + /** * The application's main frame. */ @@ -307,7 +310,7 @@ private void setUserPreferences() { * as they are both accessed through the same GUI page. * The following mnemonic keys are being used as of 30-MAR-2020: * A, B, C, E, F, H, I, L, M, N, O, P, R, S, T, V, W, / - * + *

* Note 1: the slash is used for the help, as it is normally the same key as the * ? * Note 2: the A mnemonic is used for the Advance Day button @@ -376,9 +379,9 @@ private void initMenu() { miImportCampaignPreset.setMnemonic(KeyEvent.VK_C); miImportCampaignPreset.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.ALT_DOWN_MASK)); miImportCampaignPreset.addActionListener(evt -> { - final CampaignPresetSelectionDialog campaignPresetSelectionDialog = new CampaignPresetSelectionDialog( - getFrame()); - if (!campaignPresetSelectionDialog.showDialog().isConfirmed()) { + final SelectPresetDialog campaignPresetSelectionDialog = + new SelectPresetDialog(getFrame(), true, false); + if (campaignPresetSelectionDialog.getReturnState() == PRESET_SELECTION_CANCELLED) { return; } final CampaignPreset preset = campaignPresetSelectionDialog.getSelectedPreset(); @@ -1545,7 +1548,7 @@ private void menuOptionsActionPerformed(final ActionEvent evt) { getCampaign().getUnitMarket().setOffers(unitMarket.getOffers()); miUnitMarket.setVisible(!getCampaign().getUnitMarket().getMethod().isNone()); } - + AbstractContractMarket contractMarket = getCampaign().getContractMarket(); if (contractMarket.getMethod() != newOptions.getContractMarketMethod()) { getCampaign().setContractMarket(newOptions.getContractMarketMethod().getContractMarket()); diff --git a/MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java b/MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java index 5ecc984881..899912d1f0 100644 --- a/MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/CampaignOptionsDialog.java @@ -28,12 +28,15 @@ import mekhq.gui.FileDialogs; import mekhq.gui.baseComponents.AbstractMHQValidationButtonDialog; import mekhq.gui.panes.CampaignOptionsPane; +import mekhq.gui.panes.campaignOptions.SelectPresetDialog; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.util.ResourceBundle; +import static mekhq.gui.panes.campaignOptions.SelectPresetDialog.PRESET_SELECTION_CANCELLED; + /** * @author Jay Lawson (jaylawson39 at yahoo.com) (Original Version, now largely CampaignOptionsPane) * @author Justin 'Windchild' Bowen (Current Version) @@ -105,8 +108,9 @@ protected JPanel createButtonPanel() { panel.add(new MMButton("btnLoadPreset", resources, "btnLoadPreset.text", "btnLoadPreset.toolTipText", evt -> { - final CampaignPresetSelectionDialog presetSelectionDialog = new CampaignPresetSelectionDialog(getFrame()); - if (presetSelectionDialog.showDialog().isConfirmed()) { + final SelectPresetDialog presetSelectionDialog = + new SelectPresetDialog(getFrame(), true, false); + if (presetSelectionDialog.getReturnState() != PRESET_SELECTION_CANCELLED) { applyPreset(presetSelectionDialog.getSelectedPreset()); } })); diff --git a/MekHQ/src/mekhq/gui/dialog/CampaignPresetSelectionDialog.java b/MekHQ/src/mekhq/gui/dialog/CampaignPresetSelectionDialog.java deleted file mode 100644 index 848be08b22..0000000000 --- a/MekHQ/src/mekhq/gui/dialog/CampaignPresetSelectionDialog.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2021 - 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.common.annotations.Nullable; -import mekhq.campaign.CampaignPreset; -import mekhq.gui.baseComponents.AbstractMHQButtonDialog; -import mekhq.gui.panes.CampaignPresetPane; - -import javax.swing.*; -import java.awt.*; - -public class CampaignPresetSelectionDialog extends AbstractMHQButtonDialog { - //region Variable Declarations - private CampaignPresetPane presetSelectionPanel; - //endregion Variable Declarations - - //region Constructors - public CampaignPresetSelectionDialog(final JFrame parentFrame) { - super(parentFrame, "CampaignPresetSelectionDialog", "CampaignPresetSelectionDialog.title"); - initialize(); - } - - /** - * Allows dialog to be constructed using a dialog as owner - */ - public CampaignPresetSelectionDialog(final JDialog parentDialog, final JFrame parentFrame) { - super(parentDialog, parentFrame, "CampaignPresetSelectionDialog", "CampaignPresetSelectionDialog.title"); - initialize(); - } - //endregion Constructors - - //region Getters/Setters - public CampaignPresetPane getPresetSelectionPanel() { - return presetSelectionPanel; - } - - public void setPresetSelectionPanel(final CampaignPresetPane presetSelectionPanel) { - this.presetSelectionPanel = presetSelectionPanel; - } - - /** - * @return the selected preset, or null if the dialog was cancelled or no preset was selected - */ - public @Nullable CampaignPreset getSelectedPreset() { - return getResult().isConfirmed() ? getPresetSelectionPanel().getSelectedPreset() : null; - } - //endregion Getters/Setters - - //region Initialization - @Override - protected Container createCenterPane() { - setPresetSelectionPanel(new CampaignPresetPane(getFrame())); - return getPresetSelectionPanel(); - } - //endregion Initialization - - @Override - public void setVisible(final boolean visible) { - // Only show if there are presets to select from - super.setVisible(visible && (getPresetSelectionPanel().getPresets().getModel().getSize() > 0)); - } -} diff --git a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java index 090f7a0702..db6a62606b 100644 --- a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java @@ -18,23 +18,6 @@ */ package mekhq.gui.dialog; -import java.awt.BorderLayout; -import java.awt.Container; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.io.File; -import java.io.FileInputStream; -import java.time.LocalDate; -import java.util.concurrent.CancellationException; -import java.util.concurrent.ExecutionException; - -import javax.swing.JDialog; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JProgressBar; -import javax.swing.SwingWorker; - import megamek.client.generator.RandomCallsignGenerator; import megamek.client.generator.RandomNameGenerator; import megamek.client.ui.swing.util.UIUtil; @@ -66,6 +49,21 @@ import mekhq.campaign.universe.Systems; import mekhq.campaign.universe.eras.Eras; import mekhq.gui.baseComponents.AbstractMHQDialog; +import mekhq.gui.panes.campaignOptions.SelectPresetDialog; + +import javax.swing.*; +import java.awt.*; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.FileInputStream; +import java.time.LocalDate; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; + +import static mekhq.gui.panes.campaignOptions.SelectPresetDialog.PRESET_SELECTION_CANCELLED; +import static mekhq.gui.panes.campaignOptions.SelectPresetDialog.PRESET_SELECTION_CUSTOMIZE; +import static mekhq.gui.panes.campaignOptions.SelectPresetDialog.PRESET_SELECTION_SELECT; public class DataLoadingDialog extends AbstractMHQDialog implements PropertyChangeListener { private static final MMLogger logger = MMLogger.create(DataLoadingDialog.class); @@ -290,12 +288,23 @@ public Campaign doInBackground() throws Exception { campaign = new Campaign(); // Campaign Preset - final CampaignPresetSelectionDialog presetSelectionDialog = new CampaignPresetSelectionDialog(dialog, - getFrame()); - if (presetSelectionDialog.showDialog().isCancelled()) { - return null; + final SelectPresetDialog presetSelectionDialog = + new SelectPresetDialog(getFrame(), true, true); + CampaignPreset preset; + boolean isSelect = false; + + switch (presetSelectionDialog.getReturnState()) { + case PRESET_SELECTION_CANCELLED -> { + return null; + } + case PRESET_SELECTION_SELECT -> { + preset = presetSelectionDialog.getSelectedPreset(); + isSelect = true; + } + case PRESET_SELECTION_CUSTOMIZE -> preset = presetSelectionDialog.getSelectedPreset(); + default -> throw new IllegalStateException("Unexpected value in mekhq/gui/dialog/DataLoadingDialog.java/Step 6: " + + presetSelectionDialog.getReturnState()); } - final CampaignPreset preset = presetSelectionDialog.getSelectedPreset(); // Date final LocalDate date = ((preset == null) || (preset.getDate() == null)) @@ -319,11 +328,15 @@ public Campaign doInBackground() throws Exception { setVisible(false); // Campaign Options - CampaignOptionsDialog optionsDialog = new CampaignOptionsDialog(dialog, getFrame(), campaign, true); - optionsDialog.setLocationRelativeTo(getFrame()); - optionsDialog.applyPreset(preset); - if (optionsDialog.showDialog().isCancelled()) { - return null; + if (isSelect && preset != null) { + preset.applyContinuousToCampaign(campaign); + } else { + CampaignOptionsDialog optionsDialog = new CampaignOptionsDialog(dialog, getFrame(), campaign, true); + optionsDialog.setLocationRelativeTo(getFrame()); + optionsDialog.applyPreset(preset); + if (optionsDialog.showDialog().isCancelled()) { + return null; + } } // initialize reputation diff --git a/MekHQ/src/mekhq/gui/panes/CampaignPresetPane.java b/MekHQ/src/mekhq/gui/panes/CampaignPresetPane.java deleted file mode 100644 index 864bf8f89f..0000000000 --- a/MekHQ/src/mekhq/gui/panes/CampaignPresetPane.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2021 - 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.panes; - -import java.awt.Dimension; -import java.awt.GridLayout; - -import javax.swing.DefaultListModel; -import javax.swing.JFrame; -import javax.swing.JList; -import javax.swing.JScrollPane; -import javax.swing.ListSelectionModel; - -import megamek.client.ui.preferences.JListPreference; -import megamek.client.ui.preferences.PreferencesNode; -import megamek.common.annotations.Nullable; -import megamek.logging.MMLogger; -import mekhq.campaign.CampaignPreset; -import mekhq.gui.baseComponents.AbstractMHQScrollPane; -import mekhq.gui.baseComponents.AbstractMHQScrollablePanel; -import mekhq.gui.baseComponents.DefaultMHQScrollablePanel; -import mekhq.gui.renderers.CampaignPresetRenderer; - -public class CampaignPresetPane extends AbstractMHQScrollPane { - private static final MMLogger logger = MMLogger.create(CampaignPresetPane.class); - - // region Variable Declarations - private JList presets; - // endregion Variable Declarations - - // region Constructors - public CampaignPresetPane(final JFrame frame) { - super(frame, "CampaignPresetPane", JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, - JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); - initialize(); - } - // endregion Constructors - - // region Getters/Setters - public JList getPresets() { - return presets; - } - - public void setPresets(final JList presets) { - this.presets = presets; - } - - public @Nullable CampaignPreset getSelectedPreset() { - return getPresets().getSelectedValue(); - } - // endregion Getters/Setters - - // region Initialization - @Override - protected void initialize() { - final DefaultListModel listModel = new DefaultListModel<>(); - listModel.addAll(CampaignPreset.getCampaignPresets()); - setPresets(new JList<>(listModel)); - getPresets().setName("campaignPresetList"); - getPresets().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - getPresets().setSelectedIndex(0); - getPresets().setLayoutOrientation(JList.VERTICAL); - getPresets().setCellRenderer(new CampaignPresetRenderer(getFrame())); - - final AbstractMHQScrollablePanel panel = new DefaultMHQScrollablePanel(getFrame(), - "campaignPresetPanel", new GridLayout(1, 1)); - panel.add(getPresets()); - - setViewportView(panel); - setMinimumSize(new Dimension(350, 150)); - setPreferredSize(new Dimension(500, 400)); - - try { - setPreferences(); - } catch (Exception ex) { - logger.error( - "Error setting the Campaign Preset Pane's preferences. Keeping the created pane, but this is likely to cause some oddities.", - ex); - } - } - - @Override - protected void setCustomPreferences(final PreferencesNode preferences) throws Exception { - super.setCustomPreferences(preferences); - preferences.manage(new JListPreference(getPresets())); - } - // endregion Initialization -} diff --git a/MekHQ/src/mekhq/gui/panes/campaignOptions/SelectPresetDialog.java b/MekHQ/src/mekhq/gui/panes/campaignOptions/SelectPresetDialog.java new file mode 100644 index 0000000000..342da2a033 --- /dev/null +++ b/MekHQ/src/mekhq/gui/panes/campaignOptions/SelectPresetDialog.java @@ -0,0 +1,199 @@ +/* + * 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.panes.campaignOptions; + +import megamek.logging.MMLogger; +import mekhq.MekHQ; +import mekhq.campaign.CampaignPreset; + +import javax.swing.*; +import javax.swing.GroupLayout.Alignment; +import java.awt.*; +import java.util.ResourceBundle; + +import static megamek.client.ui.WrapLayout.wordWrap; + +/** + * A dialog for selecting campaign presets. Extends {@link JDialog}. + * Keeps track of the selected preset and return state. + * Provides options to select a preset, customize a preset, or cancel the operation. + */ +public class SelectPresetDialog extends JDialog { + private static String RESOURCE_PACKAGE = "mekhq/resources/NEWCampaignOptionsDialog"; + private static final ResourceBundle resources = ResourceBundle.getBundle(RESOURCE_PACKAGE, + MekHQ.getMHQOptions().getLocale()); + + final static String OPTION_SELECT_PRESET = resources.getString("presetDialogSelect.name"); + final static String OPTION_CUSTOMIZE_PRESET = resources.getString("presetDialogCustomize.name"); + final static String OPTION_CANCEL = resources.getString("presetDialogCancel.name"); + + private static final MMLogger logger = MMLogger.create(SelectPresetDialog.class); + + private int returnState; + public static final int PRESET_SELECTION_CANCELLED = 0; + public static final int PRESET_SELECTION_SELECT = 1; + public static final int PRESET_SELECTION_CUSTOMIZE = 2; + private CampaignPreset selectedPreset; + + /** + * Returns the current return state of the dialog. + *

{@code PRESET_SELECTION_CANCELLED} = 0 + *

{@code PRESET_SELECTION_SELECT} = 1 + *

{@code PRESET_SELECTION_CUSTOMIZE} = 2 + * + * @return An integer representing the return state of the dialog. + */ + public int getReturnState() { + return returnState; + } + + /** + * @return The {@link CampaignPreset} that was selected. + */ + public CampaignPreset getSelectedPreset() { + return selectedPreset; + } + + /** + * Constructs a dialog window for selecting a campaign preset. + * + * @param frame the parent {@link JFrame} for the dialog + * @param includePresetSelectOption whether to include the option to select a preset + * @param includeCustomizePresetOption whether to include the option to customize a preset + */ + public SelectPresetDialog(JFrame frame, boolean includePresetSelectOption, boolean includeCustomizePresetOption) { + super(frame, resources.getString("presetDialog.title"), true); + returnState = PRESET_SELECTION_CANCELLED; + + setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); + + ImageIcon image = new ImageIcon("data/images/misc/megamek-splash.png"); + JLabel imageLabel = new JLabel(image); + add(imageLabel, BorderLayout.NORTH); + + JPanel centerPanel = new JPanel(); + final GroupLayout layout = new GroupLayout(centerPanel); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + centerPanel.setLayout(layout); + + JLabel descriptionLabel = new JLabel(String.format( + "

%s
", + resources.getString("presetDialog.description"))); + + final DefaultListModel campaignPresets = new DefaultListModel<>(); + campaignPresets.addAll(CampaignPreset.getCampaignPresets()); + + if (campaignPresets.isEmpty()) { + logger.error("No campaign presets found", "Error"); + } + + JComboBox comboBox = new JComboBox<>(); + comboBox.setModel(convertPresetListModelToComboBoxModel(campaignPresets)); + + DefaultListCellRenderer listRenderer = new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, + int index, boolean isSelected, + boolean cellHasFocus) { + if (value instanceof CampaignPreset preset) { + setText(preset.getTitle()); + setToolTipText(wordWrap(preset.getDescription())); + } + + setHorizontalAlignment(JLabel.CENTER); + + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + return this; + } + }; + comboBox.setRenderer(listRenderer); + + layout.setVerticalGroup( + layout.createSequentialGroup() + .addComponent(descriptionLabel) + .addComponent(comboBox) + ); + + layout.setHorizontalGroup( + layout.createParallelGroup(Alignment.LEADING) + .addComponent(descriptionLabel) + .addComponent(comboBox) + ); + + add(centerPanel, BorderLayout.CENTER); + JPanel buttonPanel = new JPanel(); + + JButton buttonSelect = new JButton(OPTION_SELECT_PRESET); + buttonSelect.setToolTipText(resources.getString("presetDialogSelect.tooltip")); + buttonSelect.addActionListener(e -> { + selectedPreset = (CampaignPreset) comboBox.getSelectedItem(); + returnState = PRESET_SELECTION_SELECT; + dispose(); + }); + buttonSelect.setEnabled(includePresetSelectOption); + buttonPanel.add(buttonSelect); + + JButton buttonCustomize = new JButton(OPTION_CUSTOMIZE_PRESET); + buttonCustomize.setToolTipText(resources.getString("presetDialogCustomize.tooltip")); + buttonCustomize.addActionListener(e -> { + selectedPreset = (CampaignPreset) comboBox.getSelectedItem(); + returnState = PRESET_SELECTION_CUSTOMIZE; + dispose(); + }); + buttonCustomize.setEnabled(includeCustomizePresetOption); + buttonPanel.add(buttonCustomize); + + JButton buttonCancel = new JButton(OPTION_CANCEL); + buttonCancel.setToolTipText(resources.getString("presetDialogCancel.tooltip")); + buttonCancel.addActionListener(e -> { + returnState = PRESET_SELECTION_CANCELLED; + dispose(); + }); + buttonPanel.add(buttonCancel); + + add(buttonPanel, BorderLayout.PAGE_END); + + pack(); + setAlwaysOnTop(true); + setResizable(false); + setLocationRelativeTo(null); + setVisible(true); + } + + /** + * Converts a {@link DefaultListModel} of {@link CampaignPreset} objects to a {@link DefaultComboBoxModel}. + * + * @param listModel The {@link DefaultListModel} to convert. + * @return The converted {@link DefaultComboBoxModel}. + */ + private DefaultComboBoxModel convertPresetListModelToComboBoxModel( + DefaultListModel listModel) { + + // Create a new DefaultComboBoxModel + DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel<>(); + + // Populate the DefaultComboBoxModel with the elements from the DefaultListModel + for (int i = 0; i < listModel.size(); i++) { + comboBoxModel.addElement(listModel.get(i)); + } + + return comboBoxModel; + } +} From 53e09f620ab7b6fa033d95a49ce35827b62adafb Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Fri, 4 Oct 2024 02:27:57 -0500 Subject: [PATCH 3/7] Refactored Daily Personnel Processing Logic. Consolidated repeated code into private methods for better readability and maintainability. Updated `processNewDayPersonnel` to handle all personnel, avoiding potential `ConcurrentModificationExceptions`. --- MekHQ/src/mekhq/campaign/Campaign.java | 305 ++++++++++++++++--------- 1 file changed, 194 insertions(+), 111 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 3a8831b2d6..fd949c8059 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -3750,9 +3750,9 @@ private void processNewDayATB() { } /** - * Processes the new day for all active personnel. + * Processes the new day for all personnel present in the campaign. *

- * This method loops through all active personnel and performs the necessary actions + * This method loops through all personnel present and performs the necessary actions * for each person for a new day. *

* The following tasks are performed for each person: @@ -3778,9 +3778,15 @@ private void processNewDayATB() { * Note: This method uses several other methods to perform the specific actions for each task. */ public void processNewDayPersonnel() { - // This MUST use getActivePersonnel as we only want to process active personnel, and - // furthermore, this allows us to add and remove personnel without issue - for (Person person : getActivePersonnel()) { + List personnelForRelationshipProcessing = new ArrayList<>(); + + for (Person person : getPersonnel()) { + if (person.getStatus().isDepartedUnit()) { + continue; + } + + personnelForRelationshipProcessing.add(person); + // Death if (getDeath().processNewDay(this, getLocalDate(), person)) { // The person has died, so don't continue to process the dead @@ -3790,146 +3796,223 @@ public void processNewDayPersonnel() { person.resetMinutesLeft(); // Reset acquisitions made to 0 person.setAcquisition(0); - if (person.needsFixing() && !getCampaignOptions().isUseAdvancedMedical()) { - person.decrementDaysToWaitForHealing(); - Person doctor = getPerson(person.getDoctorId()); - if ((doctor != null) && doctor.isDoctor()) { - if (person.getDaysToWaitForHealing() <= 0) { - addReport(healPerson(person, doctor)); - } - } else if (person.checkNaturalHealing(15)) { - addReport(person.getHyperlinkedFullTitle() + " heals naturally!"); - Unit u = person.getUnit(); - if (u != null) { - u.resetPilotAndEntity(); - } - } - } - // TODO Advanced Medical needs to go away from here later on - if (getCampaignOptions().isUseAdvancedMedical()) { - InjuryUtil.resolveDailyHealing(this, person); - Unit u = person.getUnit(); - if (u != null) { - u.resetPilotAndEntity(); - } - } - // TODO : Reset this based on hasSupportRole(false) instead of checking for each type - // TODO : person.isEngineer will need to stay, however + processAdvancedMedicalEvents(person); + // Reset edge points to the purchased value each week. This should only // apply for support personnel - combat troops reset with each new mm game - if ((person.isAdministrator() || person.isDoctor() || person.isEngineer() || person.isTech()) - && (getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY)) { - person.resetCurrentEdge(); - } - - if ((getCampaignOptions().getIdleXP() > 0) && (getLocalDate().getDayOfMonth() == 1) - && !person.getPrisonerStatus().isCurrentPrisoner()) { // Prisoners can't gain XP, while Bondsmen can gain xp - person.setIdleMonths(person.getIdleMonths() + 1); - if (person.getIdleMonths() >= getCampaignOptions().getMonthsIdleXP()) { - if (Compute.d6(2) >= getCampaignOptions().getTargetIdleXP()) { - person.awardXP(this, getCampaignOptions().getIdleXP()); - addReport(person.getHyperlinkedFullTitle() + " has gained " - + getCampaignOptions().getIdleXP() + " XP"); - } - person.setIdleMonths(0); + processWeeklyEdgeResets(person); + + processMonthlyIdleXP(person); + + // Anniversaries + processAnniversaries(person); + + // autoAwards + processMonthlyAutoAwards(person); + } + + // Divorce, Marriage + // This has to be processed separately to avoid a ConcurrentModificationException + for (Person person : personnelForRelationshipProcessing) { + processWeeklyRelationshipEvents(person); + } + } + + /** + * Processes advanced medical events for a person. + * + * @param person the {@link Person} to process advanced medical events for + */ + private void processAdvancedMedicalEvents(Person person) { + if (person.needsFixing() && !getCampaignOptions().isUseAdvancedMedical()) { + person.decrementDaysToWaitForHealing(); + Person doctor = getPerson(person.getDoctorId()); + if ((doctor != null) && doctor.isDoctor()) { + if (person.getDaysToWaitForHealing() <= 0) { + addReport(healPerson(person, doctor)); + } + } else if (person.checkNaturalHealing(15)) { + addReport(person.getHyperlinkedFullTitle() + " heals naturally!"); + Unit unit = person.getUnit(); + if (unit != null) { + unit.resetPilotAndEntity(); } } + } + // TODO Advanced Medical needs to go away from here later on + if (getCampaignOptions().isUseAdvancedMedical()) { + InjuryUtil.resolveDailyHealing(this, person); + Unit unit = person.getUnit(); + if (unit != null) { + unit.resetPilotAndEntity(); + } + } + } - // Divorce, Marriage, & Procreation - if (getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) { - getDivorce().processNewWeek(this, getLocalDate(), person, false); - getMarriage().processNewWeek(this, getLocalDate(), person); - getProcreation().processNewWeek(this, getLocalDate(), person); + /** + * Process weekly Edge resets for a given person. + * + * @param person the person for whom weekly Edge resets will be processed + */ + private void processWeeklyEdgeResets(Person person) { + if ((person.hasSupportRole(true) || person.isEngineer()) + && (getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY)) { + person.resetCurrentEdge(); + } + } - if (person.getGender().isFemale()) { - if (campaignOptions.isUseMaternityLeave()) { - if ((person.isPregnant()) - && (person.getStatus().isActive()) - && (person.getDueDate().minusWeeks(20).isEqual(getLocalDate()))) { + /** + * Process monthly idle XP for a given person. + * This method checks if the person is eligible to gain idle XP based on campaign options and current status. + * If the person meets the criteria, they may gain XP and an associated report will be added. + * + * @param person The person for whom to process monthly idle XP. + */ + private void processMonthlyIdleXP(Person person) { + if ((getCampaignOptions().getIdleXP() > 0) && (getLocalDate().getDayOfMonth() == 1) + && !person.getPrisonerStatus().isCurrentPrisoner()) { // Prisoners can't gain XP, while Bondsmen can gain xp + person.setIdleMonths(person.getIdleMonths() + 1); + if (person.getIdleMonths() >= getCampaignOptions().getMonthsIdleXP()) { + if (Compute.d6(2) >= getCampaignOptions().getTargetIdleXP()) { + person.awardXP(this, getCampaignOptions().getIdleXP()); + addReport(person.getHyperlinkedFullTitle() + " has gained " + + getCampaignOptions().getIdleXP() + " XP"); + } + person.setIdleMonths(0); + } + } + } - person.changeStatus(this, getLocalDate(), PersonnelStatus.ON_MATERNITY_LEAVE); - } + /** + * Process weekly relationship events for a given {@link Person} on Monday. + * This method triggers specific events related to divorce, marriage, procreation, and maternity leave. + * + * @param person The {@link Person} for which to process weekly relationship events + */ + private void processWeeklyRelationshipEvents(Person person) { + if (getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) { + getDivorce().processNewWeek(this, getLocalDate(), person, false); + getMarriage().processNewWeek(this, getLocalDate(), person); + getProcreation().processNewWeek(this, getLocalDate(), person); - List children = person.getGenealogy().getChildren(); + if (person.getGender().isFemale()) { + if (campaignOptions.isUseMaternityLeave()) { + if ((person.isPregnant()) + && (person.getStatus().isActive()) + && (person.getDueDate().minusWeeks(20).isEqual(getLocalDate()))) { - if ((person.getStatus().isOnMaternityLeave()) && (!children.isEmpty())) { + person.changeStatus(this, getLocalDate(), PersonnelStatus.ON_MATERNITY_LEAVE); + } - children.sort(Comparator.comparing(Person::getDateOfBirth).reversed()); + List children = person.getGenealogy().getChildren(); - if (getLocalDate().isAfter(children.get(0).getDateOfBirth().plusDays(41))) { - person.changeStatus(this, getLocalDate(), PersonnelStatus.ACTIVE); - } + if ((person.getStatus().isOnMaternityLeave()) && (!children.isEmpty())) { + LocalDate lastChildBirthDate = getYoungestChildDateOfBirth(children); + + if (currentDay.isAfter(lastChildBirthDate)) { + person.changeStatus(this, getLocalDate(), PersonnelStatus.ACTIVE); } } } } + } + } - // Anniversaries - if ((person.getRank().isOfficer()) || (!getCampaignOptions().isAnnounceOfficersOnly())) { - if ((person.getBirthday(getGameYear()).isEqual(getLocalDate())) - && (campaignOptions.isAnnounceBirthdays())) { - addReport(String.format(resources.getString("anniversaryBirthday.text"), + /** + * Process anniversaries for a given person, including birthdays and recruitment anniversaries. + * + * @param person The {@link Person} for whom the anniversaries will be processed + */ + private void processAnniversaries(Person person) { + if ((person.getRank().isOfficer()) || (!getCampaignOptions().isAnnounceOfficersOnly())) { + if ((person.getBirthday(getGameYear()).isEqual(getLocalDate())) + && (campaignOptions.isAnnounceBirthdays())) { + addReport(String.format(resources.getString("anniversaryBirthday.text"), + person.getHyperlinkedFullTitle(), + ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + person.getAge(getLocalDate()), + CLOSING_SPAN_TAG)); + } + + LocalDate recruitmentDate = person.getRecruitment(); + if (recruitmentDate != null) { + LocalDate recruitmentAnniversary = recruitmentDate.withYear(getGameYear()); + int yearsOfEmployment = (int) ChronoUnit.YEARS.between(recruitmentDate, currentDay); + + if ((recruitmentAnniversary.isEqual(getLocalDate())) + && (campaignOptions.isAnnounceRecruitmentAnniversaries())) { + addReport(String.format(resources.getString("anniversaryRecruitment.text"), person.getHyperlinkedFullTitle(), ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - person.getAge(getLocalDate()), - CLOSING_SPAN_TAG)); + yearsOfEmployment, CLOSING_SPAN_TAG, name)); } + } + } else if ((person.getAge(getLocalDate()) == 18) && (campaignOptions.isAnnounceChildBirthdays())) { + if (person.getBirthday(getGameYear()).isEqual(getLocalDate())) { + addReport(String.format(resources.getString("anniversaryBirthday.text"), + person.getHyperlinkedFullTitle(), + ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), + person.getAge(getLocalDate()), + CLOSING_SPAN_TAG)); + } + } + } - LocalDate recruitmentDate = person.getRecruitment(); - if (recruitmentDate != null) { - LocalDate recruitmentAnniversary = recruitmentDate.withYear(getGameYear()); - int yearsOfEmployment = (int) ChronoUnit.YEARS.between(recruitmentDate, currentDay); + /** + * Process monthly auto awards for a given person based on their roles and experience level. + * + * @param person the person for whom the monthly auto awards are being processed + */ + private void processMonthlyAutoAwards(Person person) { + if (getLocalDate().getDayOfMonth() == 1) { + double multiplier = 0; - if ((recruitmentAnniversary.isEqual(getLocalDate())) - && (campaignOptions.isAnnounceRecruitmentAnniversaries())) { - addReport(String.format(resources.getString("anniversaryRecruitment.text"), - person.getHyperlinkedFullTitle(), - ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - yearsOfEmployment, CLOSING_SPAN_TAG, name)); - } - } - } else if ((person.getAge(getLocalDate()) == 18) && (campaignOptions.isAnnounceChildBirthdays())) { - if (person.getBirthday(getGameYear()).isEqual(getLocalDate())) { - addReport(String.format(resources.getString("anniversaryBirthday.text"), - person.getHyperlinkedFullTitle(), - ReportingUtilities.spanOpeningWithCustomColor(MekHQ.getMHQOptions().getFontColorPositiveHexColor()), - person.getAge(getLocalDate()), - CLOSING_SPAN_TAG)); - } - } + int score = 0; - // autoAwards - if (getLocalDate().getDayOfMonth() == 1) { - double multiplier = 0; + if (person.getPrimaryRole().isSupport(true)) { + int dice = person.getExperienceLevel(this, false); - int score = 0; + if (dice > 0) { + score = Compute.d6(dice); + } - if (person.getPrimaryRole().isSupport(true)) { - int dice = person.getExperienceLevel(this, false); + multiplier += 0.5; + } - if (dice > 0) { - score = Compute.d6(dice); - } + if (person.getSecondaryRole().isSupport(true)) { + int dice = person.getExperienceLevel(this, true); - multiplier += 0.5; + if (dice > 0) { + score += Compute.d6(dice); } - if (person.getSecondaryRole().isSupport(true)) { - int dice = person.getExperienceLevel(this, true); + multiplier += 0.5; + } else if (person.getSecondaryRole().isNone()) { + multiplier += 0.5; + } - if (dice > 0) { - score += Compute.d6(dice); - } + person.changeAutoAwardSupportPoints((int) (score * multiplier)); + } + } - multiplier += 0.5; - } else if (person.getSecondaryRole().isNone()) { - multiplier += 0.5; - } + /** + * Retrieves the date of birth of the youngest child among the provided list of children. + * + * @param children the list children + * @return the date of birth of the youngest child + */ + private LocalDate getYoungestChildDateOfBirth(List children) { + LocalDate youngestChildBirthDate = LocalDate.MIN; - person.changeAutoAwardSupportPoints((int) (score * multiplier)); + for (Person child : children) { + LocalDate dateOfBirth = child.getDateOfBirth(); + if (dateOfBirth.isAfter(youngestChildBirthDate)) { + youngestChildBirthDate = dateOfBirth; } } + + return youngestChildBirthDate; } public void processNewDayUnits() { From 64f815821ff261d33d12e9f8722a609b3c5876e0 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Fri, 4 Oct 2024 02:30:44 -0500 Subject: [PATCH 4/7] Prevent idle XP accrual for inactive persons Added a check to return early in the processMonthlyIdleXP function if the person is not active. --- MekHQ/src/mekhq/campaign/Campaign.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index fd949c8059..16efae2c7d 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -3870,6 +3870,10 @@ private void processWeeklyEdgeResets(Person person) { * @param person The person for whom to process monthly idle XP. */ private void processMonthlyIdleXP(Person person) { + if (!person.getStatus().isActive()) { + return; + } + if ((getCampaignOptions().getIdleXP() > 0) && (getLocalDate().getDayOfMonth() == 1) && !person.getPrisonerStatus().isCurrentPrisoner()) { // Prisoners can't gain XP, while Bondsmen can gain xp person.setIdleMonths(person.getIdleMonths() + 1); From cb4fc0f010fdd5bebdb8c224dc661dd76415a583 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 4 Oct 2024 17:57:16 +0200 Subject: [PATCH 5/7] more gui scale replace --- MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java b/MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java index cff7915d6d..8ea617b4ec 100644 --- a/MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java @@ -34,7 +34,6 @@ 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 { @@ -154,7 +153,7 @@ private void updateStartGrid() { butText[i].append(IStartingPositions.START_LOCATION_NAMES[i]).append("
"); } - butText[currentStartPos].append(guiScaledFontHTML(uiGreen())); + butText[currentStartPos].append(colorHTML(uiGreen())); butText[currentStartPos].append("\u2B24"); for (int i = 0; i < 11; i++) { From cdb1249f747736400cc8e234514a60ba311360ab Mon Sep 17 00:00:00 2001 From: Simon Date: Sat, 5 Oct 2024 11:09:25 +0200 Subject: [PATCH 6/7] scenario chooser update, flatlaf util, more remove manual scaling --- MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java b/MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java index 8ea617b4ec..7ac1ef22a6 100644 --- a/MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/EditDeploymentDialog.java @@ -19,6 +19,7 @@ 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; @@ -153,7 +154,7 @@ private void updateStartGrid() { butText[i].append(IStartingPositions.START_LOCATION_NAMES[i]).append("
"); } - butText[currentStartPos].append(colorHTML(uiGreen())); + butText[currentStartPos].append(UIUtil.fontHTML(uiGreen())); butText[currentStartPos].append("\u2B24"); for (int i = 0; i < 11; i++) { From 976204bfa54b995b2d45159b080d13dba8a16ee5 Mon Sep 17 00:00:00 2001 From: HammerGS Date: Sat, 5 Oct 2024 11:31:51 -0600 Subject: [PATCH 7/7] Update history.txt --- MekHQ/docs/history.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MekHQ/docs/history.txt b/MekHQ/docs/history.txt index e4d6bd5ffc..e9249407d2 100644 --- a/MekHQ/docs/history.txt +++ b/MekHQ/docs/history.txt @@ -89,6 +89,9 @@ MEKHQ VERSION HISTORY: + FIX #4918: Updated OpFor Skill Generator and Added Skilled Level Parser + FIX #4929: Prevent Battle Armor allocation in TORNADO_F4 wind scenarios + PR #4931: Reworked AtB Bonus Rolls, Fixed Bug in Bulk Hire ++ PR #4981: Implemented Campaign Options IIC Preset Picker #4981 ++ PR #4984: Refactored Daily Personnel Processing Logic. ++ PR #4989: Adaptation to MM #6068 Replace Manual GUI scaling with FlatLaf Scaling 0.50.0 (2024-09-01 2000 UTC) (THIS MARKS THE START OF JAVA 17 AS THE MINIMUM REQUIRED) + PR #4332: CI Updates for windows build and normalizing