return retVal;
}
- /**
- * Determine whether the user should be nagged about unresolved scenarios on AtB
- * Stratcon tracks.
- *
- * @param campaign Campaign to check.
- * @return An informative string containing the reasons the user was nagged.
- */
- public static String nagUnresolvedContacts(Campaign campaign) {
- String sb = "";
-
- // check every track attached to an active contract for unresolved scenarios
- // to which the player must deploy forces today
- for (AtBContract contract : campaign.getActiveAtBContracts()) {
- if (contract.getStratconCampaignState() == null) {
- continue;
- }
-
- for (StratconTrackState track : contract.getStratconCampaignState().getTracks()) {
- // "scenario name, track name"
- sb = track.getScenarios().values().stream()
- .filter(scenario -> (scenario.getCurrentState() == ScenarioState.UNRESOLVED)
- && campaign.getLocalDate().equals(scenario.getDeploymentDate()))
- .map(scenario -> String.format("%s, %s\n", scenario.getName(), track.getDisplayableName()))
- .collect(Collectors.joining());
- }
- }
-
- return sb;
- }
-
/**
* Worker function that generates stratcon scenario at the given coords, for the
* given force, on the
diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java
index f711257616..a7d9f3e0c6 100644
--- a/MekHQ/src/mekhq/gui/CampaignGUI.java
+++ b/MekHQ/src/mekhq/gui/CampaignGUI.java
@@ -21,44 +21,6 @@
*/
package mekhq.gui;
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.Insets;
-import java.awt.Toolkit;
-import java.awt.event.ActionEvent;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
-import java.time.format.DateTimeFormatter;
-import java.time.temporal.TemporalAdjusters;
-import java.util.*;
-import java.util.stream.IntStream;
-import java.util.zip.GZIPOutputStream;
-
-import javax.swing.*;
-import javax.swing.UIManager.LookAndFeelInfo;
-import javax.xml.parsers.DocumentBuilder;
-
-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.client.generator.RandomUnitGenerator;
import megamek.client.ui.preferences.JWindowPreference;
@@ -67,21 +29,12 @@
import megamek.client.ui.swing.UnitLoadingDialog;
import megamek.client.ui.swing.dialog.AbstractUnitSelectorDialog;
import megamek.client.ui.swing.util.UIUtil;
-import megamek.common.Dropship;
-import megamek.common.Entity;
-import megamek.common.Jumpship;
-import megamek.common.MULParser;
-import megamek.common.MekSummaryCache;
+import megamek.common.*;
import megamek.common.annotations.Nullable;
import megamek.common.event.Subscribe;
import megamek.common.loaders.EntityLoadingException;
import megamek.logging.MMLogger;
-import mekhq.IconPackage;
-import mekhq.MHQConstants;
-import mekhq.MHQOptionsChangedEvent;
-import mekhq.MHQStaticDirectoryManager;
-import mekhq.MekHQ;
-import mekhq.Utilities;
+import mekhq.*;
import mekhq.campaign.Campaign;
import mekhq.campaign.CampaignController;
import mekhq.campaign.CampaignOptions;
@@ -102,11 +55,7 @@
import mekhq.campaign.personnel.death.ExponentialRandomDeath;
import mekhq.campaign.personnel.death.PercentageRandomDeath;
import mekhq.campaign.personnel.divorce.PercentageRandomDivorce;
-import mekhq.campaign.personnel.enums.PersonnelRole;
-import mekhq.campaign.personnel.enums.RandomDeathMethod;
-import mekhq.campaign.personnel.enums.RandomDivorceMethod;
-import mekhq.campaign.personnel.enums.RandomMarriageMethod;
-import mekhq.campaign.personnel.enums.RandomProcreationMethod;
+import mekhq.campaign.personnel.enums.*;
import mekhq.campaign.personnel.marriage.PercentageRandomMarriage;
import mekhq.campaign.personnel.procreation.AbstractProcreation;
import mekhq.campaign.personnel.procreation.PercentageRandomProcreation;
@@ -121,17 +70,29 @@
import mekhq.gui.dialog.*;
import mekhq.gui.dialog.CampaignExportWizard.CampaignExportWizardState;
import mekhq.gui.dialog.nagDialogs.*;
-import mekhq.gui.dialog.reportDialogs.CargoReportDialog;
-import mekhq.gui.dialog.reportDialogs.HangarReportDialog;
-import mekhq.gui.dialog.reportDialogs.NewsReportDialog;
-import mekhq.gui.dialog.reportDialogs.PersonnelReportDialog;
-import mekhq.gui.dialog.reportDialogs.ReputationReportDialog;
-import mekhq.gui.dialog.reportDialogs.TransportReportDialog;
-import mekhq.gui.dialog.reportDialogs.UnitRatingReportDialog;
+import mekhq.gui.dialog.reportDialogs.*;
import mekhq.gui.enums.MHQTabType;
import mekhq.gui.model.PartsTableModel;
import mekhq.io.FileType;
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 javax.swing.UIManager.LookAndFeelInfo;
+import javax.xml.parsers.DocumentBuilder;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAdjusters;
+import java.util.List;
+import java.util.*;
+import java.util.stream.IntStream;
+import java.util.zip.GZIPOutputStream;
/**
* The application's main frame.
@@ -2335,6 +2296,9 @@ public void refreshCalendar() {
getFrame().setTitle(getCampaign().getTitle());
}
+ /**
+ * Refreshes the 'funds' display on the GUI.
+ */
private void refreshFunds() {
Money funds = getCampaign().getFunds();
String inDebt = "";
@@ -2454,101 +2418,96 @@ public void undeployForce(Force f, boolean killSubs) {
// region Subscriptions
@Subscribe
- public void handleDayEnding(DayEndingEvent evt) {
- // first check for overdue loan payments - don't allow advancement until
- // these are addressed
- if (getCampaign().checkOverDueLoans()) {
- refreshFunds();
- // FIXME : Localize
- JOptionPane.showMessageDialog(null, "You must resolve overdue loans before advancing the day",
- "Overdue loans", JOptionPane.WARNING_MESSAGE);
- evt.cancel();
+ public void handleDayEnding(DayEndingEvent dayEndingEvent) {
+ if (checkForOverdueLoans(dayEndingEvent)) {
return;
}
- if (getCampaign().checkScenariosDue()) {
- JOptionPane.showMessageDialog(null, getResourceMap().getString("dialogCheckDueScenarios.text"),
- getResourceMap().getString("dialogCheckDueScenarios.title"), JOptionPane.WARNING_MESSAGE);
- evt.cancel();
+ if (checkForDueScenarios(dayEndingEvent)) {
return;
}
if (new UnmaintainedUnitsNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new PregnantCombatantNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new PrisonersNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new UntreatedPersonnelNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new EndContractNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new NoCommanderNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new InvalidFactionNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new UnableToAffordJumpNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new InsufficientAstechsNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new InsufficientAstechTimeNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new InsufficientMedicsNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (getCampaign().getLocalDate()
.equals(getCampaign().getLocalDate().with(TemporalAdjusters.lastDayOfMonth()))) {
if (new UnableToAffordExpensesNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
}
+ if (new UnableToAffordLoanPaymentNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
+ dayEndingEvent.cancel();
+ return;
+ }
+
if (getCampaign().getCampaignOptions().isUseAtB()) {
if (new ShortDeploymentNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new UnresolvedStratConContactsNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
if (new OutstandingScenariosNagDialog(getFrame(), getCampaign()).showDialog().isCancelled()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
}
@@ -2563,7 +2522,7 @@ public void handleDayEnding(DayEndingEvent evt) {
case 0:
// the user launched the turnover dialog
if (!showRetirementDefectionDialog()) {
- evt.cancel();
+ dayEndingEvent.cancel();
return;
}
case 1:
@@ -2571,7 +2530,7 @@ public void handleDayEnding(DayEndingEvent evt) {
break;
case 2:
// the user canceled
- evt.cancel();
+ dayEndingEvent.cancel();
return;
default:
throw new IllegalStateException(
@@ -2580,6 +2539,54 @@ public void handleDayEnding(DayEndingEvent evt) {
}
}
+ /**
+ * Checks if there are any due instances of the {@link Scenario} class.
+ * If the {@code checkScenariosDue()} method of the {@link Campaign} associated with the given
+ * {@link DayEndingEvent} returns {@code true}, a dialo shows up informing the user of the due
+ * scenarios, and the {@link DayEndingEvent} is canceled.
+ *
+ * @param dayEndingEvent the {@link DayEndingEvent} being checked.
+ * @return {@code true} if there are due scenarios and {@code false} otherwise.
+ */
+ private boolean checkForDueScenarios(DayEndingEvent dayEndingEvent) {
+ if (getCampaign().checkScenariosDue()) {
+ JOptionPane.showMessageDialog(null,
+ getResourceMap().getString("dialogCheckDueScenarios.text"),
+ getResourceMap().getString("dialogCheckDueScenarios.title"),
+ JOptionPane.WARNING_MESSAGE);
+
+ dayEndingEvent.cancel();
+
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks if there are any overdue loans
+ * If the {@code checkOverDueLoans()} method of the {@link Campaign} associated with the given
+ * {@link DayEndingEvent} returns {@code true}, the funds get refreshed, a dialog shows up
+ * informing the user of the overdue loans, and the {@link DayEndingEvent} is canceled.
+ *
+ * @param dayEndingEvent the {@link DayEndingEvent} being checked.
+ * @return {@code true} if there are overdue loans and {@code false} otherwise.
+ */
+ private boolean checkForOverdueLoans(DayEndingEvent dayEndingEvent) {
+ if (getCampaign().checkOverDueLoans()) {
+ refreshFunds();
+
+ JOptionPane.showMessageDialog(null,
+ getResourceMap().getString("dialogOverdueLoans.text"),
+ getResourceMap().getString("dialogOverdueLoans.title"),
+ JOptionPane.WARNING_MESSAGE);
+
+ dayEndingEvent.cancel();
+
+ return true;
+ }
+ return false;
+ }
+
@Subscribe
public void handleNewDay(NewDayEvent evt) {
refreshCalendar();
diff --git a/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java b/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java
index 67c792bfb7..b9bdbabd4d 100644
--- a/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019-2022 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2019-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -18,19 +18,6 @@
*/
package mekhq.gui.dialog;
-import java.awt.Component;
-import java.awt.Container;
-import java.awt.event.KeyEvent;
-import java.io.File;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-import java.util.Objects;
-
-import javax.swing.*;
-import javax.swing.GroupLayout.Alignment;
-
import megamek.MMConstants;
import megamek.client.ui.Messages;
import megamek.client.ui.baseComponents.MMComboBox;
@@ -49,6 +36,32 @@
import mekhq.gui.enums.ForceIconOperationalStatusStyle;
import mekhq.gui.enums.PersonnelFilterStyle;
+import javax.swing.*;
+import javax.swing.GroupLayout.Alignment;
+import java.awt.*;
+import java.awt.event.KeyEvent;
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.Objects;
+
+/**
+ * MHQOptionsDialog is a dialog that allows the user to configure various options in MegaMekHQ.
+ * It extends the {@link AbstractMHQButtonDialog} class and inherits its common dialog features.
+ * The dialog allows configuration of options related to display, colors, fonts, autosave,
+ * startup behavior, notifications, and various other miscellaneous options.
+ *
+ * To create an instance of MHQOptionsDialog, invoke one of its constructors with a frame as a parameter.
+ *
+ * Example Usage:
+ * JFrame frame = new JFrame("Main Frame");
+ * MHQOptionsDialog dialog = new MHQOptionsDialog(frame);
+ * dialog.setVisible(true);
+ *
+ * This dialog uses the following Mnemonics: C, D, M, M, S, U, W, Y
+ */
public class MHQOptionsDialog extends AbstractMHQButtonDialog {
private static final MMLogger logger = MMLogger.create(MHQOptionsDialog.class);
@@ -162,6 +175,7 @@ public class MHQOptionsDialog extends AbstractMHQButtonDialog {
private JCheckBox optionOutstandingScenariosNag;
private JCheckBox optionInvalidFactionNag;
private JCheckBox optionUnableToAffordExpensesNag;
+ private JCheckBox optionUnableToAffordLoanPaymentNag;
private JCheckBox optionUnableToAffordJumpNag;
// endregion Nag Tab
@@ -1008,6 +1022,12 @@ private JPanel createNagTab() {
.setToolTipText(resources.getString("optionUnableToAffordExpensesNag.toolTipText"));
optionUnableToAffordExpensesNag.setName("optionUnableToAffordExpensesNag");
+ optionUnableToAffordLoanPaymentNag = new JCheckBox(
+ resources.getString("optionUnableToAffordLoanPaymentNag.text"));
+ optionUnableToAffordLoanPaymentNag
+ .setToolTipText(resources.getString("optionUnableToAffordLoanPaymentNag.toolTipText"));
+ optionUnableToAffordLoanPaymentNag.setName("optionUnableToAffordLoanPaymentNag");
+
optionUnableToAffordJumpNag = new JCheckBox(resources.getString("optionUnableToAffordJumpNag.text"));
optionUnableToAffordJumpNag
.setToolTipText(resources.getString("optionUnableToAffordJumpNag.toolTipText"));
@@ -1037,6 +1057,7 @@ private JPanel createNagTab() {
.addComponent(optionOutstandingScenariosNag)
.addComponent(optionInvalidFactionNag)
.addComponent(optionUnableToAffordExpensesNag)
+ .addComponent(optionUnableToAffordLoanPaymentNag)
.addComponent(optionUnableToAffordJumpNag));
layout.setHorizontalGroup(
@@ -1055,6 +1076,7 @@ private JPanel createNagTab() {
.addComponent(optionOutstandingScenariosNag)
.addComponent(optionInvalidFactionNag)
.addComponent(optionUnableToAffordExpensesNag)
+ .addComponent(optionUnableToAffordLoanPaymentNag)
.addComponent(optionUnableToAffordJumpNag));
return panel;
@@ -1359,6 +1381,8 @@ protected void okAction() {
optionInvalidFactionNag.isSelected());
MekHQ.getMHQOptions().setNagDialogIgnore(MHQConstants.NAG_UNABLE_TO_AFFORD_EXPENSES,
optionUnableToAffordExpensesNag.isSelected());
+ MekHQ.getMHQOptions().setNagDialogIgnore(MHQConstants.NAG_UNABLE_TO_AFFORD_LOAN_PAYMENT,
+ optionUnableToAffordLoanPaymentNag.isSelected());
MekHQ.getMHQOptions().setNagDialogIgnore(MHQConstants.NAG_UNABLE_TO_AFFORD_JUMP,
optionUnableToAffordJumpNag.isSelected());
@@ -1505,6 +1529,8 @@ private void setInitialState() {
MekHQ.getMHQOptions().getNagDialogIgnore(MHQConstants.NAG_INVALID_FACTION));
optionUnableToAffordExpensesNag.setSelected(
MekHQ.getMHQOptions().getNagDialogIgnore(MHQConstants.NAG_UNABLE_TO_AFFORD_EXPENSES));
+ optionUnableToAffordLoanPaymentNag.setSelected(
+ MekHQ.getMHQOptions().getNagDialogIgnore(MHQConstants.NAG_UNABLE_TO_AFFORD_LOAN_PAYMENT));
optionUnableToAffordJumpNag.setSelected(
MekHQ.getMHQOptions().getNagDialogIgnore(MHQConstants.NAG_UNABLE_TO_AFFORD_JUMP));
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/EndContractNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/EndContractNagDialog.java
index 89948d1c68..9654f48da5 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/EndContractNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/EndContractNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2024 The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -21,24 +21,59 @@
import mekhq.MHQConstants;
import mekhq.MekHQ;
import mekhq.campaign.Campaign;
+import mekhq.campaign.mission.Contract;
import mekhq.gui.baseComponents.AbstractMHQNagDialog;
import javax.swing.*;
-import java.time.temporal.ChronoUnit;
+import java.time.LocalDate;
+/**
+ * This class represents a nag dialog displayed on the day a contract ends
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class EndContractNagDialog extends AbstractMHQNagDialog {
- private static boolean isContractEnded (Campaign campaign) {
- // we can use 'is date y after x', as once the end date has been passed,
+ private static String DIALOG_NAME = "EndContractNagDialog";
+ private static String DIALOG_TITLE = "EndContractNagDialog.title";
+ private static String DIALOG_BODY = "EndContractNagDialog.text";
+
+ /**
+ * Checks if a contract within a campaign has ended on the current date.
+ *
+ * @param campaign the {@link Campaign} containing the contracts to be checked
+ * @return {@code true} if a contract within the campaign has ended on the current date,
+ * {@code false} otherwise
+ */
+ static boolean isContractEnded(Campaign campaign) {
+ LocalDate today = campaign.getLocalDate();
+
+ // we can't use 'is date y after x', as once the end date has been passed,
// the contract is removed from the list of active contracts
- return campaign.getActiveContracts().stream()
- .anyMatch(contract -> ChronoUnit.DAYS.between(campaign.getLocalDate(), contract.getEndingDate()) == 0);
+
+ // there is no reason to use a stream here, as there won't be enough iterations to warrant it
+ for (Contract contract : campaign.getActiveContracts()) {
+ if (contract.getEndingDate().equals(today)) {
+ return true;
+ }
+ }
+
+ return false;
}
+ /**
+ * Creates a new instance of the {@link EndContractNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public EndContractNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "EndContractNagDialog", "EndContractNagDialog.title",
- "EndContractNagDialog.text", campaign, MHQConstants.NAG_CONTRACT_ENDED);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_CONTRACT_ENDED);
}
+ /**
+ * Checks if there is a nag message to display.
+ *
+ * @return {@code true} if there is a nag message to display, {@code false} otherwise
+ */
@Override
protected boolean checkNag() {
return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialog.java
index 28ba8da0e6..488c37001a 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2023 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -18,47 +18,85 @@
*/
package mekhq.gui.dialog.nagDialogs;
-import mekhq.MekHQ;
import mekhq.MHQConstants;
+import mekhq.MekHQ;
import mekhq.campaign.Campaign;
import mekhq.campaign.personnel.Person;
import mekhq.gui.baseComponents.AbstractMHQNagDialog;
import javax.swing.*;
-
+/**
+ * This class represents a nag dialog displayed when a campaign's Astech time deficit is greater than 0.
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class InsufficientAstechTimeNagDialog extends AbstractMHQNagDialog {
- //region Constructors
- public InsufficientAstechTimeNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "InsufficientAstechTimeNagDialog", "InsufficientAstechTimeNagDialog.title",
- "", campaign, MHQConstants.NAG_INSUFFICIENT_ASTECH_TIME);
- }
- //endregion Constructors
+ private static String DIALOG_NAME = "InsufficientAstechTimeNagDialog";
+ private static String DIALOG_TITLE = "InsufficientAstechTimeNagDialog.title";
+ private static String DIALOG_BODY = "InsufficientAstechTimeNagDialog.text";
- @Override
- protected boolean checkNag() {
- if (MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
- || !getCampaign().getCampaignOptions().isCheckMaintenance()) {
- return false;
- }
+ /**
+ * Checks if the given campaign has a deficit in Astech time.
+ *
+ * @param campaign the {@link Campaign} to check for an Astech time deficit
+ * @return {@code true} if the campaign has a deficit in Astech time, {@code false} otherwise
+ */
+ static boolean checkAstechTimeDeficit(Campaign campaign) {
+ return getAstechTimeDeficit(campaign) > 0;
+ }
+ /**
+ * Calculates the time deficit of Astechs needed for a given campaign.
+ *
+ * The method calculates the Astech time deficit by determining the number of Astechs
+ * required to support maintenance for all valid units in the hangar of the campaign.
+ *
+ * @param campaign the {@link Campaign} for which to calculate the Astech time deficit
+ * @return the Astech time deficit, rounded up to the nearest whole number
+ */
+ static int getAstechTimeDeficit(Campaign campaign) {
// Units are only valid if they are maintained, present, and not self crewed (as the crew
- // maintain it in that case). For each unit this is valid for, we need six astechs to assist
- // the tech for the maintenance.
- final int need = getCampaign().getHangar().getUnitsStream()
+ // maintain it in that case).
+ // For each unit, this is valid for; we need six astechs to help the tech for the maintenance.
+ final int need = campaign.getHangar().getUnitsStream()
.filter(unit -> !unit.isUnmaintained() && unit.isPresent() && !unit.isSelfCrewed())
.mapToInt(unit -> unit.getMaintenanceTime() * 6).sum();
- int available = getCampaign().getPossibleAstechPoolMinutes();
- if (getCampaign().isOvertimeAllowed()) {
- available += getCampaign().getPossibleAstechPoolOvertime();
+ int available = campaign.getPossibleAstechPoolMinutes();
+ if (campaign.isOvertimeAllowed()) {
+ available += campaign.getPossibleAstechPoolOvertime();
}
- if (available < need) {
- final int astechsNeeded = (int) Math.ceil((need - available) / (double) Person.PRIMARY_ROLE_SUPPORT_TIME);
- setDescription(String.format(resources.getString("InsufficientAstechTimeNagDialog.text"), astechsNeeded));
+ return (int) Math.ceil((need - available) / (double) Person.PRIMARY_ROLE_SUPPORT_TIME);
+ }
+
+ //region Constructors
+ /**
+ * Creates a new instance of the {@link InsufficientAstechTimeNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
+ public InsufficientAstechTimeNagDialog(final JFrame frame, final Campaign campaign) {
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_INSUFFICIENT_ASTECH_TIME);
+ }
+ //endregion Constructors
+
+ /**
+ * Checks if the Astech time deficit is greater than zero.
+ * If the count is greater than zero and the Nag dialog for the current key is not ignored,
+ * it sets the description using the specified format and returns {@code true}.
+ * Otherwise, it returns {@code false}.
+ */
+ @Override
+ protected boolean checkNag() {
+ if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
+ && checkAstechTimeDeficit(getCampaign())) {
+ setDescription(String.format(
+ resources.getString(DIALOG_BODY),
+ getAstechTimeDeficit(getCampaign())));
return true;
- } else {
- return false;
}
+
+ return false;
}
}
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialog.java
index 12f688591b..9e50a061a2 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -18,33 +18,60 @@
*/
package mekhq.gui.dialog.nagDialogs;
-import mekhq.MekHQ;
import mekhq.MHQConstants;
+import mekhq.MekHQ;
import mekhq.campaign.Campaign;
import mekhq.gui.baseComponents.AbstractMHQNagDialog;
import javax.swing.*;
+/**
+ * This class represents a nag dialog displayed when a campaign's Astech deficit is greater than 0.
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class InsufficientAstechsNagDialog extends AbstractMHQNagDialog {
+ private static String DIALOG_NAME = "InsufficientAstechsNagDialog";
+ private static String DIALOG_TITLE = "InsufficientAstechsNagDialog.title";
+ private static String DIALOG_BODY = "InsufficientAstechsNagDialog.text";
+
+ /**
+ * Checks if the count of Astechs needed is greater than zero.
+ * If so, it sets the description using the specified format and returns {@code true}.
+ * Otherwise, it returns {@code false}.
+ */
+ static boolean checkAstechsNeededCount(Campaign campaign) {
+ final int need = campaign.getAstechNeed();
+ return need > 0;
+ }
+
//region Constructors
+ /**
+ * Creates a new instance of the {@link InsufficientAstechsNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public InsufficientAstechsNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "InsufficientAstechsNagDialog", "InsufficientAstechsNagDialog.title",
- "", campaign, MHQConstants.NAG_INSUFFICIENT_ASTECHS);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_INSUFFICIENT_ASTECHS);
}
//endregion Constructors
+ /**
+ * Checks if the count of Astechs needed is greater than zero.
+ * If the count is greater than zero and the Nag dialog for the current key is not ignored,
+ * it sets the description using the specified format and returns {@code true}.
+ * Otherwise, it returns {@code false}.
+ */
@Override
protected boolean checkNag() {
- if (MekHQ.getMHQOptions().getNagDialogIgnore(getKey())) {
- return false;
- }
-
- final int need = getCampaign().getAstechNeed();
- if (need > 0) {
- setDescription(String.format(resources.getString("InsufficientAstechsNagDialog.text"), need));
+ if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
+ && checkAstechsNeededCount(getCampaign())) {
+ setDescription(String.format(
+ resources.getString(DIALOG_BODY),
+ getCampaign().getAstechNeed()));
return true;
- } else {
- return false;
}
+
+ return false;
}
}
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialog.java
index 2f7a71f7d5..8e2836d6ed 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -18,33 +18,59 @@
*/
package mekhq.gui.dialog.nagDialogs;
-import mekhq.MekHQ;
import mekhq.MHQConstants;
+import mekhq.MekHQ;
import mekhq.campaign.Campaign;
import mekhq.gui.baseComponents.AbstractMHQNagDialog;
import javax.swing.*;
+/**
+ * This class represents a nag dialog displayed when a campaign's Medic deficit is greater than 0.
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class InsufficientMedicsNagDialog extends AbstractMHQNagDialog {
+ private static String DIALOG_NAME = "InsufficientMedicsNagDialog";
+ private static String DIALOG_TITLE = "InsufficientMedicsNagDialog.title";
+ private static String DIALOG_BODY = "InsufficientMedicsNagDialog.text";
+
+ /**
+ * Checks if the count of Medics needed is greater than zero.
+ * If so, it sets the description using the specified format and returns {@code true}.
+ * Otherwise, it returns {@code false}.
+ */
+ static boolean checkMedicsNeededCount(Campaign campaign) {
+ return campaign.getMedicsNeed() > 0;
+ }
+
//region Constructors
+ /**
+ * Creates a new instance of the {@link InsufficientAstechsNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public InsufficientMedicsNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "InsufficientMedicsNagDialog", "InsufficientMedicsNagDialog.title",
- "", campaign, MHQConstants.NAG_INSUFFICIENT_MEDICS);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_INSUFFICIENT_MEDICS);
}
//endregion Constructors
+ /**
+ * Checks if the count of Medics needed is greater than zero.
+ * If the count is greater than zero and the Nag dialog for the current key is not ignored,
+ * it sets the description using the specified format and returns {@code true}.
+ * Otherwise, it returns {@code false}.
+ */
@Override
protected boolean checkNag() {
- if (MekHQ.getMHQOptions().getNagDialogIgnore(getKey())) {
- return false;
- }
-
- final int need = getCampaign().getMedicsNeed();
- if (need > 0) {
- setDescription(String.format(resources.getString("InsufficientMedicsNagDialog.text"), need));
+ if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
+ && checkMedicsNeededCount(getCampaign())) {
+ setDescription(String.format(
+ resources.getString(DIALOG_BODY),
+ getCampaign().getAstechNeed()));
return true;
- } else {
- return false;
}
+
+ return false;
}
}
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialog.java
index d3e501c089..b9a853c89b 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -28,8 +28,22 @@
import java.time.LocalDate;
import java.util.Objects;
+/**
+ * This class represents a nag dialog displayed when a campaign's faction has become extinct.
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class InvalidFactionNagDialog extends AbstractMHQNagDialog {
- private static boolean isFactionInvalid (Campaign campaign) {
+ private static String DIALOG_NAME = "InvalidFactionNagDialog";
+ private static String DIALOG_TITLE = "InvalidFactionNagDialog.title";
+ private static String DIALOG_BODY = "InvalidFactionNagDialog.text";
+
+ /**
+ * Checks if the given campaign's faction is valid.
+ *
+ * @param campaign the campaign to check
+ * @return {@code true} if the campaign's faction is invalid, {@code false} otherwise
+ */
+ static boolean isFactionInvalid(Campaign campaign) {
Faction campaignFaction = campaign.getFaction();
if (!campaign.getFaction().validIn(campaign.getLocalDate())) {
@@ -40,27 +54,61 @@ private static boolean isFactionInvalid (Campaign campaign) {
// FS and LA campaigns won't trigger the above conditional, because those factions aren't technically ended when the FedSuns forms
// they just become dormant.
if (Objects.equals(campaignFaction.getShortName(), "LA")) {
- // the dates picked are chosen as these are when mhq does the bulk of the faction ownership transfers
- return ((campaign.getLocalDate().isAfter(LocalDate.of(3040, 1, 18)))
- && (campaign.getLocalDate().isBefore(LocalDate.of(3067, 4, 20))));
+ return lyranAllianceSpecialHandler(campaign);
}
// this is another special handler for FedSuns as they're the main culprit behind the issue of users having invalid factions.
if (Objects.equals(campaignFaction.getShortName(), "FS")) {
- return ((campaign.getLocalDate().isAfter(LocalDate.of(3040, 1, 18)))
- && (campaign.getLocalDate().isBefore(LocalDate.of(3057, 9, 18))));
+ return federatedSunsSpecialHandler(campaign);
}
return false;
}
+ /**
+ * Checks if the given campaign falls within the inactive date range of the Federated Suns.
+ *
+ * @param campaign The current campaign.
+ * @return Returns {@code true} if the campaign falls within the active date range, otherwise {@code false}.
+ */
+ static boolean federatedSunsSpecialHandler(Campaign campaign) {
+ boolean isAfterActiveDate = campaign.getLocalDate().isAfter(LocalDate.of(3040, 1, 18));
+ boolean isBeforeInactiveDate = campaign.getLocalDate().isBefore(LocalDate.of(3057, 9, 18));
+
+ return isAfterActiveDate && isBeforeInactiveDate;
+ }
+
+ /**
+ * Checks if the given campaign falls within the inactive date range of the Lyran Alliance.
+ *
+ * @param campaign The current campaign.
+ * @return Returns {@code true} if the campaign falls within the active date range, otherwise {@code false}.
+ */
+ static boolean lyranAllianceSpecialHandler(Campaign campaign) {
+ // the dates picked are chosen as these are when mhq does the bulk of the faction ownership transfers
+ boolean isAfterActiveDate = campaign.getLocalDate().isAfter(LocalDate.of(3040, 1, 18));
+ boolean isBeforeInactiveDate = campaign.getLocalDate().isBefore(LocalDate.of(3067, 4, 20));
+
+ return isAfterActiveDate && isBeforeInactiveDate;
+ }
+
//region Constructors
+ /**
+ * Creates a new instance of the {@link InvalidFactionNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public InvalidFactionNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "InvalidFactionNagDialog", "InvalidFactionNagDialog.title",
- "InvalidFactionNagDialog.text", campaign, MHQConstants.NAG_INVALID_FACTION);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_INVALID_FACTION);
}
-
//endregion Constructors
+
+ /**
+ * Checks if there is a nag message to display.
+ *
+ * @return {@code true} if there is a nag message to display, {@code false} otherwise
+ */
@Override
protected boolean checkNag() {
return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) && (isFactionInvalid(getCampaign()));
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialog.java
index 9e24e4c555..61d8458bb4 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - 2024 The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -25,16 +25,41 @@
import javax.swing.*;
+/**
+ * This class represents a nag dialog displayed when the campaign has no assigned commander
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class NoCommanderNagDialog extends AbstractMHQNagDialog {
- private static boolean isCommanderMissing (Campaign campaign) {
+ private static String DIALOG_NAME = "NoCommanderNagDialog";
+ private static String DIALOG_TITLE = "NoCommanderNagDialog.title";
+ private static String DIALOG_BODY = "NoCommanderNagDialog.text";
+
+
+ /**
+ * Checks if the given {@link Campaign} is missing a commander.
+ *
+ * @param campaign the campaign to check for a missing commander
+ * @return {@code true} if the campaign is missing a commander, otherwise {@code false}
+ */
+ static boolean isCommanderMissing (Campaign campaign) {
return (campaign.getFlaggedCommander() == null);
}
+ /**
+ * Creates a new instance of the {@link EndContractNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public NoCommanderNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "NoCommanderNagDialog", "NoCommanderNagDialog.title",
- "NoCommanderNagDialog.text", campaign, MHQConstants.NAG_NO_COMMANDER);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_NO_COMMANDER);
}
+ /**
+ * Checks if there is a nag message to display.
+ *
+ * @return {@code true} if there is a nag message to display, {@code false} otherwise
+ */
@Override
protected boolean checkNag() {
return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialog.java
index 3513d2b07b..69a4f9deea 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -18,30 +18,71 @@
*/
package mekhq.gui.dialog.nagDialogs;
-import mekhq.MekHQ;
import mekhq.MHQConstants;
+import mekhq.MekHQ;
import mekhq.campaign.Campaign;
+import mekhq.campaign.mission.AtBContract;
+import mekhq.campaign.mission.AtBScenario;
import mekhq.gui.baseComponents.AbstractMHQNagDialog;
import javax.swing.*;
+import java.time.LocalDate;
+import java.util.List;
+/**
+ * This class represents a nag dialog displayed when the campaign one or more unresolved scenarios.
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class OutstandingScenariosNagDialog extends AbstractMHQNagDialog {
+ private static String DIALOG_NAME = "OutstandingScenariosNagDialog";
+ private static String DIALOG_TITLE = "OutstandingScenariosNagDialog.title";
+ private static String DIALOG_BODY = "OutstandingScenariosNagDialog.text";
+
+ /**
+ * Checks if there are any outstanding scenarios in the given campaign.
+ * An outstanding scenario is defined as a scenario whose date is the same as the current date.
+ *
+ * @param campaign the campaign to check for outstanding scenarios
+ * @return {@code true} if there are outstanding scenarios, {@code false} otherwise
+ */
+ static boolean checkForOutstandingScenarios(Campaign campaign) {
+ List activeContracts = campaign.getActiveAtBContracts(true);
+
+ LocalDate today = campaign.getLocalDate();
+
+ for (AtBContract contract : activeContracts) {
+ for (AtBScenario scenario : contract.getCurrentAtBScenarios()) {
+ LocalDate scenarioDate = scenario.getDate();
+
+ if (scenarioDate.equals(today)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
//region Constructors
+ /**
+ * Creates a new instance of the {@link OutstandingScenariosNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public OutstandingScenariosNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "OutstandingScenariosNagDialog", "OutstandingScenariosNagDialog.title",
- "OutstandingScenariosNagDialog.text", campaign,
- MHQConstants.NAG_OUTSTANDING_SCENARIOS);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_OUTSTANDING_SCENARIOS);
}
//endregion Constructors
+ /**
+ * Checks if there is a nag message to display.
+ *
+ * @return {@code true} if there is a nag message to display, {@code false} otherwise
+ */
@Override
protected boolean checkNag() {
- // If this isn't ignored, check all active AtB contracts for current AtB scenarios whose
- // date is today
return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
- && getCampaign().getActiveAtBContracts(true).stream()
- .anyMatch(contract -> contract.getCurrentAtBScenarios().stream()
- .anyMatch(scenario -> (scenario.getDate() != null)
- && scenario.getDate().isEqual(getCampaign().getLocalDate())));
+ && checkForOutstandingScenarios(getCampaign());
}
}
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialog.java
index 11302d3cc2..89d7bfbffe 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -21,29 +21,69 @@
import mekhq.MHQConstants;
import mekhq.MekHQ;
import mekhq.campaign.Campaign;
+import mekhq.campaign.force.Force;
+import mekhq.campaign.mission.Mission;
import mekhq.campaign.personnel.Person;
+import mekhq.campaign.unit.Unit;
import mekhq.gui.baseComponents.AbstractMHQNagDialog;
import javax.swing.*;
+/**
+ * This class represents a nag dialog displayed when the campaign has an active mission and has one
+ * or more pregnant personnel assigned to the TOE
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class PregnantCombatantNagDialog extends AbstractMHQNagDialog {
- private static boolean isPregnantCombatant(Campaign campaign) {
+ private static String DIALOG_NAME = "PregnantCombatantNagDialog";
+ private static String DIALOG_TITLE = "PregnantCombatantNagDialog.title";
+ private static String DIALOG_BODY = "PregnantCombatantNagDialog.text";
+
+ /**
+ * Checks if there is a pregnant combatant in the provided campaign. Combatants are defined as
+ * personnel assigned to a {@link Unit} in the TO&E during an active {@link Mission}
+ *
+ * @param campaign the campaign to check
+ * @return {@code true} if there is a pregnant combatant, {@code false} otherwise
+ */
+ static boolean isPregnantCombatant(Campaign campaign) {
if (campaign.getActiveMissions(false).isEmpty()) {
return false;
}
- return campaign.getActivePersonnel().stream()
- .filter(Person::isPregnant)
- .anyMatch(p -> ((p.getUnit() != null) && (p.getUnit().getForceId() != -1)));
+ // there is no reason to use a stream here, as there won't be enough iterations to warrant it
+ for (Person person : campaign.getActivePersonnel()) {
+ if (person.isPregnant()) {
+ Unit unit = person.getUnit();
+
+ if (unit != null) {
+ if (unit.getForceId() != Force.FORCE_NONE) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
}
//region Constructors
+ /**
+ * Creates a new instance of the {@link PregnantCombatantNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public PregnantCombatantNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "PregnantCombatantNagDialog", "PregnantCombatantNagDialog.title",
- "PregnantCombatantNagDialog.text", campaign, MHQConstants.NAG_PREGNANT_COMBATANT);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_PREGNANT_COMBATANT);
}
//endregion Constructors
+ /**
+ * Checks if there is a nag message to display.
+ *
+ * @return {@code true} if there is a nag message to display, {@code false} otherwise
+ */
@Override
protected boolean checkNag() {
return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/PrisonersNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/PrisonersNagDialog.java
index 096d2ec598..32616835b5 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/PrisonersNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/PrisonersNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -21,24 +21,52 @@
import mekhq.MHQConstants;
import mekhq.MekHQ;
import mekhq.campaign.Campaign;
+import mekhq.campaign.mission.Mission;
import mekhq.gui.baseComponents.AbstractMHQNagDialog;
import javax.swing.*;
+/**
+ * This class represents a nag dialog displayed when the campaign has retained prisoners of war
+ * outside an active {@link Mission}
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class PrisonersNagDialog extends AbstractMHQNagDialog {
- private static boolean hasPrisoners (Campaign campaign) {
+ private static String DIALOG_NAME = "PrisonersNagDialog";
+ private static String DIALOG_TITLE = "PrisonersNagDialog.title";
+ private static String DIALOG_BODY = "PrisonersNagDialog.text";
+
+ /**
+ * Checks if the given campaign has any prisoners.
+ *
+ * @param campaign the campaign to check for prisoners
+ * @return {@code true} if the campaign has prisoners, {@code false} otherwise
+ */
+ static boolean hasPrisoners (Campaign campaign) {
if (!campaign.hasActiveContract()) {
return !campaign.getCurrentPrisoners().isEmpty();
}
+
return false;
}
+
//region Constructors
+ /**
+ * Creates a new instance of the {@link PrisonersNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public PrisonersNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "PrisonersNagDialog", "PrisonersNagDialog.title",
- "PrisonersNagDialog.text", campaign, MHQConstants.NAG_PRISONERS);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_PRISONERS);
}
-
//endregion Constructors
+
+ /**
+ * Checks if there is a nag message to display.
+ *
+ * @return {@code true} if there is a nag message to display, {@code false} otherwise
+ */
@Override
protected boolean checkNag() {
return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey()) && hasPrisoners(getCampaign());
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialog.java
index f558f1f64b..a008f9c424 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -18,31 +18,71 @@
*/
package mekhq.gui.dialog.nagDialogs;
-import mekhq.MekHQ;
import mekhq.MHQConstants;
+import mekhq.MekHQ;
import mekhq.campaign.Campaign;
+import mekhq.campaign.mission.AtBContract;
import mekhq.gui.baseComponents.AbstractMHQNagDialog;
import javax.swing.*;
import java.time.DayOfWeek;
+/**
+ * This class represents a nag dialog displayed when the campaign does not meet the deployment
+ * levels required by their active {@link AtBContract}
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class ShortDeploymentNagDialog extends AbstractMHQNagDialog {
+ private static String DIALOG_NAME = "ShortDeploymentNagDialog";
+ private static String DIALOG_TITLE = "ShortDeploymentNagDialog.title";
+ private static String DIALOG_BODY = "ShortDeploymentNagDialog.text";
+
+ /**
+ * Checks if the deployment requirements are met for a given campaign.
+ *
+ * @param campaign the campaign to check the deployment requirements for
+ * @return {@code true} if the deployment requirements are met, {@code false} otherwise
+ */
+ static boolean checkDeploymentRequirementsMet(Campaign campaign) {
+ if (!campaign.getLocation().isOnPlanet()) {
+ return false;
+ }
+
+ // this prevents the nag from spamming daily
+ if (campaign.getLocalDate().getDayOfWeek() != DayOfWeek.SUNDAY) {
+ return false;
+ }
+
+ // There is no need to use a stream here, as the number of iterations doesn't warrant it.
+ for (AtBContract contract : campaign.getActiveAtBContracts()) {
+ if (campaign.getDeploymentDeficit(contract) > 0) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
//region Constructors
+ /**
+ * Creates a new instance of the {@link ShortDeploymentNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public ShortDeploymentNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "ShortDeploymentNagDialog", "ShortDeploymentNagDialog.title",
- "ShortDeploymentNagDialog.text", campaign, MHQConstants.NAG_SHORT_DEPLOYMENT);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_SHORT_DEPLOYMENT);
}
//endregion Constructors
+ /**
+ * Checks if there is a nag message to display.
+ *
+ * @return {@code true} if there is a nag message to display, {@code false} otherwise
+ */
@Override
protected boolean checkNag() {
- if (MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
- || !getCampaign().getLocation().isOnPlanet()
- || (getCampaign().getLocalDate().getDayOfWeek() != DayOfWeek.SUNDAY)) {
- return false;
- }
-
- return getCampaign().getActiveAtBContracts().stream()
- .anyMatch(contract -> getCampaign().getDeploymentDeficit(contract) > 0);
+ return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
+ && checkDeploymentRequirementsMet(getCampaign());
}
}
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialog.java
index 1d468df5d5..bf62fb31db 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -27,25 +27,71 @@
import javax.swing.*;
+/**
+ * This class represents a nag dialog displayed when the campaign does not have enough funds to
+ * cover monthly expenses
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class UnableToAffordExpensesNagDialog extends AbstractMHQNagDialog {
- private static boolean isUnableToAffordExpenses (Campaign campaign) {
- FinancialReport financialReport = FinancialReport.calculate(campaign);
+ private static String DIALOG_NAME = "UnableToAffordExpensesNagDialog";
+ private static String DIALOG_TITLE = "UnableToAffordExpensesNagDialog.title";
+ private static String DIALOG_BODY = "UnableToAffordExpensesNagDialog.text";
- Money deficit = financialReport.getTotalLiabilities().plus(financialReport.getMonthlyExpenses());
+ /**
+ * Determines whether the given campaign is unable to afford its monthly expenses.
+ *
+ * @param campaign the ongoing campaign
+ * @return {@code true} if the campaign's funds are less than the total deficit, {@code false} otherwise
+ */
+ static boolean isUnableToAffordExpenses (Campaign campaign) {
+ Money deficit = getMonthlyExpenses(campaign);
+ // check if the campaign's funds are less than the total deficit
return campaign.getFunds().isLessThan(deficit);
}
+ /**
+ * Calculates and returns the monthly expenses of a given campaign.
+ *
+ * @param campaign the campaign for which to calculate the monthly expenses
+ * @return the monthly expenses as a {@link Money} object
+ */
+ static Money getMonthlyExpenses(Campaign campaign) {
+ // calculate a financial report which includes the monthly expenses
+ FinancialReport financialReport = FinancialReport.calculate(campaign);
+
+ // get the total monthly expenses
+ return financialReport.getMonthlyExpenses();
+ }
+
//region Constructors
+ /**
+ * Creates a new instance of the {@link ShortDeploymentNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public UnableToAffordExpensesNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "UnableToAffordExpensesNagDialog", "UnableToAffordExpensesNagDialog.title",
- "UnableToAffordExpensesNagDialog.text", campaign, MHQConstants.NAG_UNABLE_TO_AFFORD_EXPENSES);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_UNABLE_TO_AFFORD_EXPENSES);
}
//endregion Constructors
+ /**
+ * Checks if the campaign is able to afford its monthly expenses.
+ * If the campaign is unable to afford monthly expenses and the Nag dialog for the current key
+ * is not ignored, it sets the description using the specified format and returns {@code true}.
+ * Otherwise, it returns {@code false}.
+ */
@Override
protected boolean checkNag() {
- return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
- && isUnableToAffordExpenses(getCampaign());
+ if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
+ && isUnableToAffordExpenses(getCampaign())) {
+ setDescription(String.format(
+ resources.getString(DIALOG_BODY),
+ getMonthlyExpenses(getCampaign()).toAmountAndSymbolString()));
+ return true;
+ }
+
+ return false;
}
}
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java
index dc04fd84d1..df9648df95 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -21,25 +21,77 @@
import mekhq.MHQConstants;
import mekhq.MekHQ;
import mekhq.campaign.Campaign;
+import mekhq.campaign.finances.Money;
import mekhq.gui.baseComponents.AbstractMHQNagDialog;
import javax.swing.*;
+/**
+ * This class represents a nag dialog displayed when the campaign can't afford its next jump
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class UnableToAffordJumpNagDialog extends AbstractMHQNagDialog {
- private static boolean isUnableToAffordNextJump (Campaign campaign) {
- return campaign.getFunds().isLessThan(campaign.calculateCostPerJump(true, campaign.getCampaignOptions().isEquipmentContractBase()));
+ private static String DIALOG_NAME = "UnableToAffordJumpNagDialog";
+ private static String DIALOG_TITLE = "UnableToAffordJumpNagDialog.title";
+ private static String DIALOG_BODY = "UnableToAffordJumpNagDialog.text";
+
+ /**
+ * Checks if the campaign is unable to afford the cost of the next jump.
+ *
+ * @param campaign The campaign for which to check the affordability of the next jump.
+ * @return {@code true} if the campaign is unable to afford the cost of the next jump, {@code false} otherwise.
+ */
+ static boolean isUnableToAffordNextJump (Campaign campaign) {
+ Money currentFunds = campaign.getFunds();
+ Money nextJumpCost = getNextJumpCost(campaign);
+
+ return currentFunds.isLessThan(nextJumpCost);
+ }
+
+ /**
+ * Calculates the cost of the next jump for a given campaign.
+ *
+ * This method determines the cost of the next jump by using the campaign's
+ * options and calculates the cost per jump based on whether the contract pays
+ * based on the value of the units in the campaign's TO&E.
+ *
+ * @param campaign the campaign for which to calculate the next jump cost
+ * @return the cost of the next jump for the campaign
+ */
+ static Money getNextJumpCost(Campaign campaign) {
+ boolean isContractPayBasedOnToeUnitsValue = campaign.getCampaignOptions().isEquipmentContractBase();
+
+ return campaign.calculateCostPerJump(true, isContractPayBasedOnToeUnitsValue);
}
//region Constructors
+ /**
+ * Creates a new instance of the {@link UnableToAffordJumpNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public UnableToAffordJumpNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "UnableToAffordJumpNagDialog", "UnableToAffordJumpNagDialog.title",
- "UnableToAffordJumpNagDialog.text", campaign, MHQConstants.NAG_UNABLE_TO_AFFORD_JUMP);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_UNABLE_TO_AFFORD_JUMP);
}
//endregion Constructors
+ /**
+ * Checks if the campaign is able to afford its next jump.
+ * If the campaign is unable to afford its next jump and the Nag dialog for the current key is
+ * not ignored, it sets the description using the specified format and returns {@code true}.
+ * Otherwise, it returns {@code false}.
+ */
@Override
protected boolean checkNag() {
- return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
- && isUnableToAffordNextJump(getCampaign());
+ if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
+ && isUnableToAffordNextJump(getCampaign())) {
+ setDescription(String.format(
+ resources.getString(DIALOG_BODY),
+ getNextJumpCost(getCampaign()).toAmountAndSymbolString()));
+ return true;
+ }
+
+ return false;
}
}
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialog.java
new file mode 100644
index 0000000000..0b86ce9ae7
--- /dev/null
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialog.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2021-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.nagDialogs;
+
+import mekhq.MHQConstants;
+import mekhq.MekHQ;
+import mekhq.campaign.Campaign;
+import mekhq.campaign.finances.Loan;
+import mekhq.campaign.finances.Money;
+import mekhq.gui.baseComponents.AbstractMHQNagDialog;
+
+import javax.swing.*;
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * This class represents a nag dialog displayed when the campaign cannot afford its next loan payment
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
+public class UnableToAffordLoanPaymentNagDialog extends AbstractMHQNagDialog {
+ private static String DIALOG_NAME = "UnableToAffordLoanPaymentNagDialog";
+ private static String DIALOG_TITLE = "UnableToAffordLoanPaymentNagDialog.title";
+ private static String DIALOG_BODY = "UnableToAffordLoanPaymentNagDialog.text";
+
+ /**
+ * Determines if the campaign is unable to afford its due loan payments.
+ *
+ * @param campaign the campaign for which the loan payment affordability needs to be checked
+ * @return {@code true} if the campaign is unable to afford the loan payment, {@code false} otherwise
+ */
+ static boolean isUnableToAffordLoanPayment(Campaign campaign) {
+ Money totalPaymentsDue = getTotalPaymentsDue(campaign);
+
+ // check if the campaign's funds are less than the total payments due tomorrow
+ return campaign.getFunds().isLessThan(totalPaymentsDue);
+ }
+
+ /**
+ * Calculates the total payments due tomorrow, across all current loans
+ *
+ * @param campaign the campaign for which to calculate the total payments due
+ * @return the total payments due as a {@link Money} object
+ */
+ static Money getTotalPaymentsDue(Campaign campaign) {
+ // gets the list of the campaign's current loans
+ List loans = campaign.getFinances().getLoans();
+
+ // gets tomorrow's date
+ LocalDate tomorrow = campaign.getLocalDate().plusDays(1);
+
+ // initialize the total loan payment due tomorrow as zero
+ Money totalPaymentsDue = Money.zero();
+
+ // iterate over all loans
+ for (Loan loan : loans) {
+ // if a loan payment is due tomorrow, add its payment amount to the total payments due
+ if (loan.getNextPayment().equals(tomorrow)) {
+ totalPaymentsDue = totalPaymentsDue.plus(loan.getPaymentAmount());
+ }
+ }
+ return totalPaymentsDue;
+ }
+
+ //region Constructors
+ /**
+ * Creates a new instance of the {@link UnableToAffordLoanPaymentNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
+ public UnableToAffordLoanPaymentNagDialog(final JFrame frame, final Campaign campaign) {
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_UNABLE_TO_AFFORD_LOAN_PAYMENT);
+ }
+ //endregion Constructors
+
+ /**
+ * Checks if the campaign is able to afford its next loan payment.
+ * If the campaign is unable to afford its next loan payment and the Nag dialog for the current
+ * key is not ignored, it sets the description using the specified format and returns {@code true}.
+ * Otherwise, it returns {@code false}.
+ */
+ @Override
+ protected boolean checkNag() {
+ if (!MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
+ && isUnableToAffordLoanPayment(getCampaign())) {
+ setDescription(String.format(
+ resources.getString(DIALOG_BODY),
+ getTotalPaymentsDue(getCampaign()).toAmountAndSymbolString()));
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialog.java
index a80792d50b..6ef957e934 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -26,9 +26,23 @@
import javax.swing.*;
+/**
+ * A dialog that displays a nag message if there are unmaintained units in the campaign's hangar.
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class UnmaintainedUnitsNagDialog extends AbstractMHQNagDialog {
- private boolean checkHanger() {
- for (Unit u : getCampaign().getHangar().getUnits()) {
+ private static String DIALOG_NAME = "UnmaintainedUnitsNagDialog";
+ private static String DIALOG_TITLE = "UnmaintainedUnitsNagDialog.title";
+ private static String DIALOG_BODY = "UnmaintainedUnitsNagDialog.text";
+
+ /**
+ * Checks if there are any unmaintained units in the given campaign's hangar.
+ *
+ * @param campaign the {@link Campaign} containing the hangar to check
+ * @return {@code true} if there are unmaintained units in the hangar, {@code false} otherwise
+ */
+ static boolean checkHanger(Campaign campaign) {
+ for (Unit u : campaign.getHangar().getUnits()) {
if ((u.isUnmaintained()) && (!u.isSalvage())) {
return true;
}
@@ -36,16 +50,26 @@ private boolean checkHanger() {
return false;
}
+ /**
+ * Creates a new instance of the {@link UnmaintainedUnitsNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
//region Constructors
public UnmaintainedUnitsNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "UnmaintainedUnitsNagDialog", "UnmaintainedUnitsNagDialog.title",
- "UnmaintainedUnitsNagDialog.text", campaign, MHQConstants.NAG_UNMAINTAINED_UNITS);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_UNMAINTAINED_UNITS);
}
//endregion Constructors
+ /**
+ * Checks if there is a nag message to display.
+ *
+ * @return {@code true} if there is a nag message to display, {@code false} otherwise
+ */
@Override
protected boolean checkNag() {
return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
- && checkHanger();
+ && checkHanger(getCampaign());
}
}
diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialog.java
index 2d017eee0c..b1483beb1a 100644
--- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialog.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2021-2023 - The MegaMek Team. All Rights Reserved.
+ * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved.
*
* This file is part of MekHQ.
*
@@ -18,36 +18,102 @@
*/
package mekhq.gui.dialog.nagDialogs;
-import mekhq.MekHQ;
import mekhq.MHQConstants;
+import mekhq.MekHQ;
import mekhq.campaign.Campaign;
-import mekhq.campaign.stratcon.StratconRulesManager;
+import mekhq.campaign.mission.AtBContract;
+import mekhq.campaign.stratcon.StratconScenario;
+import mekhq.campaign.stratcon.StratconScenario.ScenarioState;
+import mekhq.campaign.stratcon.StratconTrackState;
import mekhq.gui.baseComponents.AbstractMHQNagDialog;
import javax.swing.*;
+/**
+ * This class represents a nag dialog displayed when the campaign has outstanding StratCon contacts
+ * It extends the {@link AbstractMHQNagDialog} class.
+ */
public class UnresolvedStratConContactsNagDialog extends AbstractMHQNagDialog {
+ private static String DIALOG_NAME = "UnresolvedStratConContactsNagDialog";
+ private static String DIALOG_TITLE = "UnresolvedStratConContactsNagDialog.title";
+ private static String DIALOG_BODY = "UnresolvedStratConContactsNagDialog.text";
+
+ /**
+ * Checks if the given campaign has unresolved contact nags.
+ *
+ * @param campaign the campaign to check for unresolved contacts
+ * @return a string indicating whether the campaign has unresolved contacts or not
+ */
+ boolean hasUnresolvedContacts(Campaign campaign) {
+ String unresolvedContacts = nagUnresolvedContacts(campaign);
+
+ if (unresolvedContacts.isEmpty()) {
+ return false;
+ } else {
+ setDescription(String.format(resources.getString(DIALOG_BODY), unresolvedContacts));
+ return true;
+ }
+ }
+
+ /**
+ * Determine whether the user should be nagged about unresolved scenarios on AtB
+ * StratCon tracks.
+ *
+ * @param campaign Campaign to check.
+ * @return An informative string containing the reasons the user was nagged.
+ */
+ static String nagUnresolvedContacts(Campaign campaign) {
+ if (!campaign.getCampaignOptions().isUseStratCon()) {
+ return "";
+ }
+
+ StringBuilder unresolvedContacts = new StringBuilder();
+
+ // check every track attached to an active contract for unresolved scenarios
+ // to which the player must deploy forces today
+ for (AtBContract contract : campaign.getActiveAtBContracts()) {
+ if (contract.getStratconCampaignState() == null) {
+ continue;
+ }
+
+ for (StratconTrackState track : contract.getStratconCampaignState().getTracks()) {
+ // "scenario name, track name"
+ for (StratconScenario scenario : track.getScenarios().values()) {
+ if ((scenario.getCurrentState() == ScenarioState.UNRESOLVED)
+ && (campaign.getLocalDate().equals(scenario.getDeploymentDate()))) {
+ String resolvedScenario = String.format("%s, %s\n",
+ scenario.getName(),
+ track.getDisplayableName());
+
+ unresolvedContacts.append(resolvedScenario);
+ }
+ }
+ }
+ }
+
+ return unresolvedContacts.toString();
+ }
+
//region Constructors
+ /**
+ * Creates a new instance of the {@link UnresolvedStratConContactsNagDialog} class.
+ *
+ * @param frame the parent JFrame for the dialog
+ * @param campaign the {@link Campaign} associated with the dialog
+ */
public UnresolvedStratConContactsNagDialog(final JFrame frame, final Campaign campaign) {
- super(frame, "UnresolvedStratConContactsNagDialog", "UnresolvedStratConContactsNagDialog.title",
- "", campaign, MHQConstants.NAG_UNRESOLVED_STRATCON_CONTACTS);
+ super(frame, DIALOG_NAME, DIALOG_TITLE, DIALOG_BODY, campaign, MHQConstants.NAG_UNRESOLVED_STRATCON_CONTACTS);
}
//endregion Constructors
+ /**
+ * Checks if there is a nag message to display.
+ *
+ * @return {@code true} if there is a nag message to display, {@code false} otherwise
+ */
@Override
protected boolean checkNag() {
- if (MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
- || !getCampaign().getCampaignOptions().isUseStratCon()) {
- return false;
- }
-
- final String text = StratconRulesManager.nagUnresolvedContacts(getCampaign());
-
- if (text.isEmpty()) {
- return false;
- } else {
- setDescription(String.format(resources.getString("UnresolvedStratConContactsNagDialog.text"), text));
- return true;
- }
+ return !MekHQ.getMHQOptions().getNagDialogIgnore(getKey())
+ && hasUnresolvedContacts(getCampaign());
}
}
diff --git a/MekHQ/unittests/mekhq/campaign/event/DayEndingEventTest.java b/MekHQ/unittests/mekhq/campaign/event/DayEndingEventTest.java
new file mode 100644
index 0000000000..39e8c78e79
--- /dev/null
+++ b/MekHQ/unittests/mekhq/campaign/event/DayEndingEventTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.campaign.event;
+
+import mekhq.campaign.Campaign;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+
+/**
+ * This class is responsible for unit testing the cancellation of {@link DayEndingEvent}.
+ */
+class DayEndingEventTest {
+ /**
+ * Unit test to verify if a {@link DayEndingEvent} can be canceled.
+ */
+ @Test
+ void checkDayEndingEventCancellable() {
+ // Creates a mock instance of the Campaign class.
+ Campaign mockCampaign = mock(Campaign.class);
+
+ // Creates a new DayEndingEvent associated with the mock Campaign.
+ DayEndingEvent dayEndingEvent = new DayEndingEvent(mockCampaign);
+
+ // Asserts that the isCancellable() method of the created DayEndingEvent returns true.
+ // If it does not, this test will fail.
+ assertTrue(dayEndingEvent.isCancellable());
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/EndContractNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/EndContractNagDialogTest.java
new file mode 100644
index 0000000000..8993646200
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/EndContractNagDialogTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.mission.Contract;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+import static mekhq.gui.dialog.nagDialogs.EndContractNagDialog.isContractEnded;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link EndContractNagDialog} class.
+ * It contains test methods for various scenarios related to contract expiration.
+ */
+public class EndContractNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+ private LocalDate today;
+ private Contract contract1, contract2;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ today = LocalDate.now();
+ contract1 = mock(Contract.class);
+ contract2 = mock(Contract.class);
+
+ // When the Campaign mock calls 'getLocalDate()' return today's date
+ when(campaign.getLocalDate()).thenReturn(today);
+ }
+
+ // In the following tests the isContractEnded() method is called, and its response is
+ // checked against expected behavior
+
+ @Test
+ void noActiveContracts() {
+ when(campaign.getActiveContracts()).thenReturn(new ArrayList<>());
+ assertFalse(isContractEnded(campaign));
+ }
+
+ @Test
+ void oneActiveContractEndsTomorrow() {
+ when(campaign.getActiveContracts()).thenReturn(List.of(contract1));
+ when(contract1.getEndingDate()).thenReturn(today.plusDays(1));
+ assertFalse(isContractEnded(campaign));
+ }
+
+ @Test
+ void oneActiveContractEndsToday() {
+ when(campaign.getActiveContracts()).thenReturn(List.of(contract1));
+ when(contract1.getEndingDate()).thenReturn(today);
+ assertTrue(isContractEnded(campaign));
+ }
+
+ @Test
+ void twoActiveContractsOneEndsTomorrowOneEndsToday() {
+ when(campaign.getActiveContracts()).thenReturn(List.of(contract1, contract2));
+ when(contract1.getEndingDate()).thenReturn(today.plusDays(1));
+ when(contract2.getEndingDate()).thenReturn(today);
+ assertTrue(isContractEnded(campaign));
+ }
+
+ @Test
+ void twoActiveContractsBothEndToday() {
+ when(campaign.getActiveContracts()).thenReturn(List.of(contract1, contract2));
+ when(contract1.getEndingDate()).thenReturn(today);
+ assertTrue(isContractEnded(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialogTest.java
new file mode 100644
index 0000000000..084ceb73e0
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechTimeNagDialogTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.Hangar;
+import mekhq.campaign.unit.Unit;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.stream.Stream;
+
+import static mekhq.gui.dialog.nagDialogs.InsufficientAstechTimeNagDialog.checkAstechTimeDeficit;
+import static mekhq.gui.dialog.nagDialogs.InsufficientAstechTimeNagDialog.getAstechTimeDeficit;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class contains test cases for the {@link InsufficientAstechTimeNagDialog} class.
+ * It tests the different combinations of Astech requirements and verifies the behavior of the
+ * {@code getAstechTimeDeficit()} and {@code checkAstechTimeDeficit()} methods.
+ */
+class InsufficientAstechTimeNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+ private Hangar hangar;
+ private Unit unit1, unit2;
+ private int possibleAstechMinutes;
+ private int possibleAstechOvertimeMinutes;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ hangar = mock(Hangar.class);
+ unit1 = mock(Unit.class);
+ unit2 = mock(Unit.class);
+ possibleAstechMinutes = 2880; // This is the equivalent of one full team
+ possibleAstechOvertimeMinutes = 1440; // As is this
+
+ // Stub the getHangar() method to return the hangar mock
+ when(campaign.getHangar()).thenReturn(hangar);
+ }
+
+ /**
+ * Initializes a unit with the given parameters.
+ *
+ * @param unit the unit to be initialized
+ * @param isUnmaintained a boolean indicating if the unit is unmaintained
+ * @param isPresent a boolean indicating if the unit is present
+ * @param isSelfCrewed a boolean indicating if the unit is self-crewed
+ * @param maintenanceTime the maintenance time for the unit
+ */
+ private void initiateUnit(Unit unit, boolean isUnmaintained, boolean isPresent, boolean isSelfCrewed, int maintenanceTime) {
+ when(unit.isUnmaintained()).thenReturn(isUnmaintained);
+ when(unit.isPresent()).thenReturn(isPresent);
+ when(unit.isSelfCrewed()).thenReturn(isSelfCrewed);
+ when(unit.getMaintenanceTime()).thenReturn(maintenanceTime);
+ }
+
+ /**
+ * Processes units and Astech time based on the given parameters.
+ *
+ * @param isOvertimeAllowed {@code true} if overtime is allowed, {@code false} otherwise
+ */
+ private void processUnitsAndAstechTime(boolean isOvertimeAllowed) {
+ // Prepare a stream of the unit mocks
+ Stream unitStream = Stream.of(unit1, unit2);
+
+ // Stub the getUnitsStream() method to return the stream of unit mocks
+ when(hangar.getUnitsStream()).thenReturn(unitStream);
+
+ // Calculate possible Astech Minutes
+ when(campaign.getPossibleAstechPoolMinutes()).thenReturn(possibleAstechMinutes);
+
+ // Calculate overtime minutes
+ when(campaign.isOvertimeAllowed()).thenReturn(isOvertimeAllowed);
+ when(campaign.getPossibleAstechPoolOvertime()).thenReturn(possibleAstechOvertimeMinutes);
+ }
+
+ // In the following tests the getAstechTimeDeficit() method is called, and its response is
+ // checked against expected behavior
+
+ @Test
+ void testAstechTimeDeficitCalculationNoOvertime() {
+ // Initiate Units
+ initiateUnit(unit1, false, true, false, 60);
+ initiateUnit(unit2, false, true, false, 60);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(false);
+
+ // Assert results equals expected value
+ assertEquals(-4, getAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testAstechTimeDeficitCalculationWithOvertime() {
+ // Initiate Units
+ initiateUnit(unit1, false, true, false, 60);
+ initiateUnit(unit2, false, true, false, 60);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(true);
+
+ // Assert results equals expected value
+ assertEquals(-7, getAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testInsufficientAstechTimeDeficitCalculationNoOvertime() {
+ // Initiate Units
+ initiateUnit(unit1, false, true, false, 6000);
+ initiateUnit(unit2, false, true, false, 6000);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(false);
+
+ // Assert results equals expected value
+ assertEquals(144, getAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testInsufficientAstechTimeDeficitCalculationWithInsufficientOvertime() {
+ // Initiate Units
+ initiateUnit(unit1, false, true, false, 6000);
+ initiateUnit(unit2, false, true, false, 6000);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(true);
+
+ // Assert results equals expected value
+ assertEquals(141, getAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testInsufficientAstechTimeDeficitCalculationWithSufficientOvertime() {
+ // Initiate Units
+ initiateUnit(unit1, false, true, false, 300);
+ initiateUnit(unit2, false, true, false, 300);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(true);
+
+ // Assert results equals expected value
+ assertEquals(-1, getAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testAstechTimeDeficitCalculationOneUnitUnmaintained() {
+ // Initiate Units
+ initiateUnit(unit1, true, true, false, 60);
+ initiateUnit(unit2, false, true, false, 60);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(false);
+
+ // Assert results equals expected value
+ assertEquals(-5, getAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testAstechTimeDeficitCalculationTwoUnitsUnmaintained() {
+ // Initiate Units
+ initiateUnit(unit1, true, true, false, 60);
+ initiateUnit(unit2, true, true, false, 60);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(false);
+
+ // Assert results equals expected value
+ assertEquals(-6, getAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testAstechTimeDeficitCalculationOneUnitAbsent() {
+ // Initiate Units
+ initiateUnit(unit1, false, false, false, 60);
+ initiateUnit(unit2, false, true, false, 60);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(false);
+
+ // Assert results equals expected value
+ assertEquals(-5, getAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testAstechTimeDeficitCalculationTwoUnitsAbsent() {
+ // Initiate Units
+ initiateUnit(unit1, false, false, false, 60);
+ initiateUnit(unit2, false, false, false, 60);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(false);
+
+ // Assert results equals expected value
+ assertEquals(-6, getAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testAstechTimeDeficitCalculationOneUnitSelfCrewed() {
+ // Initiate Units
+ initiateUnit(unit1, false, true, true, 60);
+ initiateUnit(unit2, false, true, false, 60);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(false);
+
+ // Assert results equals expected value
+ assertEquals(-5, getAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testAstechTimeDeficitCalculationTwoUnitsSelfCrewed() {
+ // Initiate Units
+ initiateUnit(unit1, false, true, true, 60);
+ initiateUnit(unit2, false, true, true, 60);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(false);
+
+ // Assert results equals expected value
+ assertEquals(-6, getAstechTimeDeficit(campaign));
+ }
+
+ // In the following tests the checkAstechTimeDeficit() method is called, and its response is
+ // checked against expected behavior
+
+ @Test
+ void testAstechTimeDeficitCheckNegativeDeficit() {
+ // Initiate Units
+ initiateUnit(unit1, false, true, false, 60);
+ initiateUnit(unit2, false, true, false, 60);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(false);
+
+ // Assert results equals expected value
+ assertFalse(checkAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testAstechTimeDeficitCheckPositiveDeficit() {
+ // Initiate Units
+ initiateUnit(unit1, false, true, false, 6000);
+ initiateUnit(unit2, false, true, false, 6000);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(false);
+
+ // Assert results equals expected value
+ assertTrue(checkAstechTimeDeficit(campaign));
+ }
+
+ @Test
+ void testAstechTimeDeficitCheckZeroDeficit() {
+ // Initiate Units
+ initiateUnit(unit1, false, true, false, 240);
+ initiateUnit(unit2, false, true, false, 240);
+
+ // Stream Units and process Astech Time
+ processUnitsAndAstechTime(false);
+
+ // Assert results equals expected value
+ assertFalse(checkAstechTimeDeficit(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialogTest.java
new file mode 100644
index 0000000000..8664a38a28
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientAstechsNagDialogTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static mekhq.gui.dialog.nagDialogs.InsufficientAstechsNagDialog.checkAstechsNeededCount;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class contains test cases for the {@link InsufficientAstechsNagDialog} class.
+ * It tests the different combinations of Astech requirements and verifies the behavior of the
+ * {@code checkAstechsNeededCount()} method.
+ */
+class InsufficientAstechsNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ campaign = mock(Campaign.class);
+ }
+
+ // In the following tests the checkAstechsNeededCount() method is called, and its response is
+ // checked against expected behavior
+
+ @Test
+ void noAstechsNeeded() {
+ when(campaign.getAstechNeed()).thenReturn(0);
+ assertFalse(checkAstechsNeededCount(campaign));
+ }
+
+ @Test
+ void oneAstechNeeded() {
+ when(campaign.getAstechNeed()).thenReturn(1);
+ assertTrue(checkAstechsNeededCount(campaign));
+ }
+
+ @Test
+ void negativeAstechsNeeded() {
+ when(campaign.getAstechNeed()).thenReturn(-1);
+ assertFalse(checkAstechsNeededCount(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialogTest.java
new file mode 100644
index 0000000000..00a3a9fdbd
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InsufficientMedicsNagDialogTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static mekhq.gui.dialog.nagDialogs.InsufficientMedicsNagDialog.checkMedicsNeededCount;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class contains test cases for the {@link InsufficientMedicsNagDialogTest} class.
+ * It tests the different combinations of Medic requirements and verifies the behavior of the
+ * {@code checkMedicsNeededCount()} method.
+ */
+class InsufficientMedicsNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ campaign = mock(Campaign.class);
+ }
+
+ // In the following tests the checkMedicsNeededCount() method is called, and its response is
+ // checked against expected behavior
+
+ @Test
+ void noMedicsNeeded() {
+ when(campaign.getMedicsNeed()).thenReturn(0);
+ assertFalse(checkMedicsNeededCount(campaign));
+ }
+
+ @Test
+ void oneMedicNeeded() {
+ when(campaign.getMedicsNeed()).thenReturn(1);
+ assertTrue(checkMedicsNeededCount(campaign));
+ }
+
+ @Test
+ void negativeMedicsNeeded() {
+ when(campaign.getMedicsNeed()).thenReturn(-1);
+ assertFalse(checkMedicsNeededCount(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialogTest.java
new file mode 100644
index 0000000000..5316f020ed
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/InvalidFactionNagDialogTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.universe.Faction;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+
+import static mekhq.gui.dialog.nagDialogs.InvalidFactionNagDialog.federatedSunsSpecialHandler;
+import static mekhq.gui.dialog.nagDialogs.InvalidFactionNagDialog.isFactionInvalid;
+import static mekhq.gui.dialog.nagDialogs.InvalidFactionNagDialog.lyranAllianceSpecialHandler;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link InvalidFactionNagDialog} class.
+ *
+ * It tests the different combinations of unit states and verifies the behavior of the
+ * {@code isFactionInvalid()}, {@code lyranAllianceSpecialHandler()}, and
+ * {@code federatedSunsSpecialHandler()} methods.
+ */
+class InvalidFactionNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+ private Faction faction;
+ private LocalDate dateValid;
+ private LocalDate dateInvalid;
+
+ /**
+ * Sets up the necessary dependencies and configurations before running the test methods.
+ * Runs once before all tests
+ */
+ @BeforeEach
+ public void setup() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ faction = mock(Faction.class);
+
+ dateValid = LocalDate.of(3151, 1, 1);
+ dateInvalid = LocalDate.of(1936, 1, 1);
+
+ // When the Campaign mock calls 'getFaction()' return the mocked faction
+ when(campaign.getFaction()).thenReturn(faction);
+ }
+
+ // In the following tests, the beginning of the isFactionInvalid() method is called, and its
+ // response is checked against expected behavior
+
+ @Test
+ public void validDate() {
+ when(faction.validIn(dateValid)).thenReturn(true);
+ when(campaign.getLocalDate()).thenReturn(dateValid);
+
+ assertFalse(isFactionInvalid(campaign));
+ }
+
+ @Test
+ public void invalidDate() {
+ when(faction.validIn(dateInvalid)).thenReturn(false);
+ when(campaign.getLocalDate()).thenReturn(dateInvalid);
+
+ assertTrue(isFactionInvalid(campaign));
+ }
+
+ // In the following tests, the lyranAllianceSpecialHandler() method is called, and its response
+ // is checked against expected behavior
+
+ @Test
+ public void lyranAllianceSpecialHandlerInvalidDate() {
+ when(campaign.getLocalDate()).thenReturn(LocalDate.of(3067, 4, 19));
+ assertTrue(lyranAllianceSpecialHandler(campaign));
+ }
+
+ @Test
+ public void lyranAllianceSpecialHandlerBeforeActiveDate() {
+ when(campaign.getLocalDate()).thenReturn(LocalDate.of(3040, 1, 17));
+ assertFalse(lyranAllianceSpecialHandler(campaign));
+ }
+
+ @Test
+ public void lyranAllianceSpecialHandlerAfterInactiveDate() {
+ when(campaign.getLocalDate()).thenReturn(LocalDate.of(3067, 4, 21));
+ assertFalse(lyranAllianceSpecialHandler(campaign));
+ }
+
+ // In the following tests, the federatedSunsSpecialHandler() method is called, and its response
+ // is checked against expected behavior
+
+ @Test
+ public void federatedSunsSpecialHandlerInvalidDate() {
+ when(campaign.getLocalDate()).thenReturn(LocalDate.of(3057, 9, 17));
+
+ assertTrue(federatedSunsSpecialHandler(campaign));
+ }
+
+ @Test
+ public void federatedSunsSpecialHandlerBeforeActiveDate() {
+ when(campaign.getLocalDate()).thenReturn(LocalDate.of(3040, 1, 17));
+
+ assertFalse(federatedSunsSpecialHandler(campaign));
+ }
+
+ @Test
+ public void federatedSunsSpecialHandlerAfterInactiveDate() {
+ when(campaign.getLocalDate()).thenReturn(LocalDate.of(3057, 9, 19));
+
+ assertFalse(federatedSunsSpecialHandler(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialogTest.java
new file mode 100644
index 0000000000..37f725ede8
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/NoCommanderNagDialogTest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.personnel.Person;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static mekhq.gui.dialog.nagDialogs.NoCommanderNagDialog.isCommanderMissing;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link NoCommanderNagDialog} class.
+ * It contains test methods for various scenarios related to commander assignment.
+ */
+class NoCommanderNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+ private Person commander;
+ private Person commanderNull;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ commander = mock(Person.class);
+ commanderNull = null;
+ }
+
+ // In the following tests the isCommanderMissing() method is called, and its response is checked
+ // against expected behavior
+
+ @Test
+ void commanderPresent() {
+ when(campaign.getFlaggedCommander()).thenReturn(commander);
+ assertFalse(isCommanderMissing(campaign));
+ }
+
+ @Test
+ void commanderMissing() {
+ when(campaign.getFlaggedCommander()).thenReturn(commanderNull);
+ assertTrue(isCommanderMissing(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialogTest.java
new file mode 100644
index 0000000000..a7a5f8e6b5
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/OutstandingScenariosNagDialogTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.mission.AtBContract;
+import mekhq.campaign.mission.AtBScenario;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+import static mekhq.gui.dialog.nagDialogs.OutstandingScenariosNagDialog.checkForOutstandingScenarios;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link OutstandingScenariosNagDialog} class.
+ * It contains tests for various scenarios related to the {@code checkForOutstandingScenarios} method
+ */
+class OutstandingScenariosNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+ private AtBContract contract;
+ private AtBScenario scenario1, scenario2;
+ private LocalDate today;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ contract = mock(AtBContract.class);
+ scenario1 = mock(AtBScenario.class);
+ scenario2 = mock(AtBScenario.class);
+ today = LocalDate.now();
+
+ // When the Campaign mock calls 'getLocalDate()' return today's date
+ when(campaign.getLocalDate()).thenReturn(today);
+ }
+
+ /**
+ * Initializes an {@link AtBContract} containing two instances of {@link AtBScenario}.
+ */
+ private void initializeContractWithTwoScenarios() {
+ when(campaign.getActiveAtBContracts(true)).thenReturn(List.of(contract));
+ when(contract.getCurrentAtBScenarios()).thenReturn(List.of(scenario1, scenario2));
+ }
+
+ // In the following tests the checkForOutstandingScenarios() method is called, and its response
+ // is checked against expected behavior
+
+ @Test
+ void noContracts() {
+ when(campaign.getActiveAtBContracts(true)).thenReturn(new ArrayList<>());
+
+ assertFalse(checkForOutstandingScenarios(campaign));
+ }
+
+ @Test
+ void noScenarios() {
+ when(campaign.getActiveAtBContracts(true)).thenReturn(List.of(contract));
+ when(contract.getCurrentAtBScenarios()).thenReturn(new ArrayList<>());
+
+ assertFalse(checkForOutstandingScenarios(campaign));
+ }
+
+ @Test
+ void noOutstandingScenarios() {
+ initializeContractWithTwoScenarios();
+
+ when(scenario1.getDate()).thenReturn(today.plusDays(1));
+ when(scenario2.getDate()).thenReturn(today.plusDays(1));
+
+ assertFalse(checkForOutstandingScenarios(campaign));
+ }
+
+ @Test
+ void oneOutstandingScenarioFirst() {
+ initializeContractWithTwoScenarios();
+
+ when(scenario1.getDate()).thenReturn(today);
+ when(scenario2.getDate()).thenReturn(today.plusDays(1));
+
+ assertTrue(checkForOutstandingScenarios(campaign));
+ }
+
+ @Test
+ void oneOutstandingScenarioSecond() {
+ initializeContractWithTwoScenarios();
+
+ when(scenario1.getDate()).thenReturn(today.plusDays(1));
+ when(scenario2.getDate()).thenReturn(today);
+
+ assertTrue(checkForOutstandingScenarios(campaign));
+ }
+
+ @Test
+ void twoOutstandingScenarios() {
+ initializeContractWithTwoScenarios();
+
+ when(scenario1.getDate()).thenReturn(today);
+ when(scenario2.getDate()).thenReturn(today);
+
+ assertTrue(checkForOutstandingScenarios(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialogTest.java
new file mode 100644
index 0000000000..343aa7987d
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PregnantCombatantNagDialogTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.force.Force;
+import mekhq.campaign.mission.Mission;
+import mekhq.campaign.personnel.Person;
+import mekhq.campaign.unit.Unit;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static mekhq.gui.dialog.nagDialogs.PregnantCombatantNagDialog.isPregnantCombatant;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link PregnantCombatantNagDialog} class.
+ * It contains tests for various scenarios related to the {@code isPregnantCombatant} method
+ */
+class PregnantCombatantNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+ private Mission mission;
+ private Person personNotPregnant;
+ private Person personPregnant;
+ private Unit unit;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ mission = mock(Mission.class);
+ personNotPregnant = mock(Person.class);
+ personPregnant = mock(Person.class);
+ unit = mock(Unit.class);
+
+ // Stubs
+ when(personNotPregnant.isPregnant()).thenReturn(false);
+ when(personPregnant.isPregnant()).thenReturn(true);
+ }
+
+ // In the following tests the isPregnantCombatant() method is called, and its response is
+ // checked against expected behavior
+
+ @Test
+ void noActiveMission() {
+ when(campaign.getActiveMissions(false)).thenReturn(new ArrayList<>());
+ assertFalse(isPregnantCombatant(campaign));
+ }
+
+ @Test
+ void activeMissionsNoPregnancy() {
+ when(campaign.getActiveMissions(false)).thenReturn(List.of(mission));
+ when(campaign.getActivePersonnel()).thenReturn(List.of(personNotPregnant));
+
+ assertFalse(isPregnantCombatant(campaign));
+ }
+
+ @Test
+ void activeMissionsPregnancyNoUnit() {
+ when(campaign.getActiveMissions(false)).thenReturn(List.of(mission));
+ when(campaign.getActivePersonnel()).thenReturn(List.of(personNotPregnant, personPregnant));
+
+ when(personPregnant.getUnit()).thenReturn(null);
+
+ assertFalse(isPregnantCombatant(campaign));
+ }
+
+ @Test
+ void activeMissionsPregnancyNoForce() {
+ when(campaign.getActiveMissions(false)).thenReturn(List.of(mission));
+ when(campaign.getActivePersonnel()).thenReturn(List.of(personNotPregnant, personPregnant));
+
+ when(personPregnant.getUnit()).thenReturn(unit);
+ when(unit.getForceId()).thenReturn(Force.FORCE_NONE);
+
+ assertFalse(isPregnantCombatant(campaign));
+ }
+
+ @Test
+ void activeMissionsPregnancyYesUnitYesForce() {
+ when(campaign.getActiveMissions(false)).thenReturn(List.of(mission));
+ when(campaign.getActivePersonnel()).thenReturn(List.of(personNotPregnant, personPregnant));
+
+ when(personPregnant.getUnit()).thenReturn(unit);
+ when(unit.getForceId()).thenReturn(1);
+
+ assertTrue(isPregnantCombatant(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PrisonersNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PrisonersNagDialogTest.java
new file mode 100644
index 0000000000..7e8af2e374
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/PrisonersNagDialogTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.personnel.Person;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static mekhq.gui.dialog.nagDialogs.PrisonersNagDialog.hasPrisoners;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link PrisonersNagDialog} class.
+ * It contains tests for various scenarios related to the {@code hasPrisoners} method
+ */
+class PrisonersNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ }
+
+ // In the following tests the hasPrisoners() method is called, and its response is checked
+ // against expected behavior
+
+ @Test
+ void activeContract() {
+ when(campaign.hasActiveContract()).thenReturn(true);
+
+ assertFalse(hasPrisoners(campaign));
+ }
+
+ @Test
+ void noActiveContractNoPrisoners() {
+ when(campaign.hasActiveContract()).thenReturn(false);
+ when(campaign.getCurrentPrisoners()).thenReturn(new ArrayList<>());
+
+ assertFalse(hasPrisoners(campaign));
+ }
+
+ @Test
+ void noActiveContractPrisoners() {
+ Person prisoner = mock(Person.class);
+
+ when(campaign.hasActiveContract()).thenReturn(false);
+ when(campaign.getCurrentPrisoners()).thenReturn(List.of(prisoner));
+
+ assertTrue(hasPrisoners(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialogTest.java
new file mode 100644
index 0000000000..25ab03cd9b
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/ShortDeploymentNagDialogTest.java
@@ -0,0 +1,131 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.CurrentLocation;
+import mekhq.campaign.mission.AtBContract;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+import static mekhq.gui.dialog.nagDialogs.ShortDeploymentNagDialog.checkDeploymentRequirementsMet;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link ShortDeploymentNagDialog} class.
+ * It contains tests for various scenarios related to the {@code checkDeploymentRequirementsMet}
+ * method
+ */
+public class ShortDeploymentNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+ // I know 'location' can be converted to a local variable, but it makes sense to keep all the
+ // mock objects in one place
+ private CurrentLocation location;
+ private LocalDate monday, sunday;
+ private AtBContract contract;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+
+ location = mock(CurrentLocation.class);
+
+ monday = LocalDate.of(2024, 10, 7);
+ sunday = LocalDate.of(2024, 10, 6);
+
+ contract = mock(AtBContract.class);
+
+ // Stubs
+ when(campaign.getLocation()).thenReturn(location);
+ }
+
+ // In the following tests the checkDeploymentRequirementsMet() method is called, and its
+ // response is checked against expected behavior
+
+ @Test
+ void notOnPlanet() {
+ when(campaign.getLocation().isOnPlanet()).thenReturn(false);
+
+ assertFalse(checkDeploymentRequirementsMet(campaign));
+ }
+
+ @Test
+ void notSunday() {
+ when(campaign.getLocation().isOnPlanet()).thenReturn(true);
+ when(campaign.getLocalDate()).thenReturn(monday);
+
+ assertFalse(checkDeploymentRequirementsMet(campaign));
+ }
+
+ @Test
+ void noContract() {
+ when(campaign.getLocation().isOnPlanet()).thenReturn(true);
+ when(campaign.getLocalDate()).thenReturn(sunday);
+
+ when(campaign.getActiveAtBContracts()).thenReturn(new ArrayList<>());
+
+ assertFalse(checkDeploymentRequirementsMet(campaign));
+ }
+
+ @Test
+ void noDeploymentDeficit() {
+ when(campaign.getLocation().isOnPlanet()).thenReturn(true);
+ when(campaign.getLocalDate()).thenReturn(sunday);
+
+ when(campaign.getActiveAtBContracts()).thenReturn(List.of(contract));
+ when(campaign.getDeploymentDeficit(contract)).thenReturn(0);
+
+ assertFalse(checkDeploymentRequirementsMet(campaign));
+ }
+
+ @Test
+ void negativeDeploymentDeficit() {
+ when(campaign.getLocation().isOnPlanet()).thenReturn(true);
+ when(campaign.getLocalDate()).thenReturn(sunday);
+
+ when(campaign.getActiveAtBContracts()).thenReturn(List.of(contract));
+ when(campaign.getDeploymentDeficit(contract)).thenReturn(-3);
+
+ assertFalse(checkDeploymentRequirementsMet(campaign));
+ }
+
+ @Test
+ void positiveDeploymentDeficit() {
+ when(campaign.getLocation().isOnPlanet()).thenReturn(true);
+ when(campaign.getLocalDate()).thenReturn(sunday);
+
+ when(campaign.getActiveAtBContracts()).thenReturn(List.of(contract));
+ when(campaign.getDeploymentDeficit(contract)).thenReturn(1);
+
+ assertTrue(checkDeploymentRequirementsMet(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialogTest.java
new file mode 100644
index 0000000000..f74dce5be0
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordExpensesNagDialogTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.CampaignOptions;
+import mekhq.campaign.Hangar;
+import mekhq.campaign.Warehouse;
+import mekhq.campaign.finances.Finances;
+import mekhq.campaign.finances.FinancialReport;
+import mekhq.campaign.finances.Money;
+import mekhq.campaign.unit.Unit;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static mekhq.gui.dialog.nagDialogs.UnableToAffordExpensesNagDialog.isUnableToAffordExpenses;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link UnableToAffordExpensesNagDialog} class.
+ * It contains tests for various scenarios related to the {@code isUnableToAffordExpenses} method
+ */
+class UnableToAffordExpensesNagDialogTest {
+ // Mock objects for the tests
+ // I know some of these can be converted to a local variable, but it makes sense to keep all the
+ // mock objects in one place
+ private Campaign campaign;
+ private CampaignOptions campaignOptions;
+ private Finances finances;
+ private Unit unit;
+ private Hangar hangar;
+ private Warehouse warehouse;
+ private FinancialReport report;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ campaignOptions = mock(CampaignOptions.class);
+
+ finances = mock(Finances.class);
+
+ unit = mock(Unit.class);
+ hangar = mock(Hangar.class);
+ hangar.addUnit(unit);
+
+ warehouse = mock(Warehouse.class);
+
+ report = mock(FinancialReport.class);
+
+ // Stubs
+ when(campaign.getFinances()).thenReturn(finances);
+ when(campaign.getHangar()).thenReturn(hangar);
+ when(campaign.getWarehouse()).thenReturn(warehouse);
+ when(campaign.getCampaignOptions()).thenReturn(campaignOptions);
+ }
+
+ // In the following tests the isUnableToAffordExpenses() method is called, and its response is
+ // checked against expected behavior
+
+ @Test
+ void canAffordExpenses() {
+ when(campaign.getFunds()).thenReturn(Money.of(2));
+ when(report.getMonthlyExpenses()).thenReturn(Money.of(1));
+
+ assertFalse(isUnableToAffordExpenses(campaign));
+ }
+
+ @Test
+ void cannotAffordExpenses() {
+ when(campaign.getFunds()).thenReturn(Money.of(1));
+ when(report.getMonthlyExpenses()).thenReturn(Money.of(2));
+
+ assertFalse(isUnableToAffordExpenses(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java
new file mode 100644
index 0000000000..70cbe2cb4f
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.CampaignOptions;
+import mekhq.campaign.finances.Money;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static mekhq.gui.dialog.nagDialogs.UnableToAffordJumpNagDialog.getNextJumpCost;
+import static mekhq.gui.dialog.nagDialogs.UnableToAffordJumpNagDialog.isUnableToAffordNextJump;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link UnableToAffordJumpNagDialog} class.
+ * It contains tests for various scenarios related to the {@code isUnableToAffordNextJump} method
+ */
+class UnableToAffordJumpNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+ private CampaignOptions options;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ options = mock(CampaignOptions.class);
+
+ // Stubs
+ when(campaign.getCampaignOptions()).thenReturn(options);
+ }
+
+ // In the following tests the canAffordNextJump() method is called, and its response is checked
+ // against expected behavior
+
+ @Test
+ void canAffordNextJump() {
+ when(campaign.getFunds()).thenReturn(Money.of(5));
+ when(getNextJumpCost(campaign)).thenReturn(Money.of(1));
+
+ assertFalse(isUnableToAffordNextJump(campaign));
+ }
+
+ @Test
+ void cannotAffordNextJump() {
+ when(campaign.getFunds()).thenReturn(Money.of(1));
+ when(getNextJumpCost(campaign)).thenReturn(Money.of(5));
+
+ assertTrue(isUnableToAffordNextJump(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialogTest.java
new file mode 100644
index 0000000000..0bea2932fa
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordLoanPaymentNagDialogTest.java
@@ -0,0 +1,137 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.finances.Finances;
+import mekhq.campaign.finances.Loan;
+import mekhq.campaign.finances.Money;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+
+import static mekhq.gui.dialog.nagDialogs.UnableToAffordLoanPaymentNagDialog.getTotalPaymentsDue;
+import static mekhq.gui.dialog.nagDialogs.UnableToAffordLoanPaymentNagDialog.isUnableToAffordLoanPayment;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link UnableToAffordLoanPaymentNagDialog} class.
+ * It contains tests for various scenarios related to the {@code getTotalPaymentsDue} and
+ * {@code isUnableToAffordLoanPayment} methods
+ */
+class UnableToAffordLoanPaymentNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+ private LocalDate today;
+ private Finances finances;
+ private Loan firstLoan, secondLoan;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ today = LocalDate.now();
+ finances = mock(Finances.class);
+ firstLoan = mock(Loan.class);
+ secondLoan = mock(Loan.class);
+
+ // Stubs
+ when(campaign.getFinances()).thenReturn(finances);
+ when(campaign.getLocalDate()).thenReturn(today);
+
+ when(firstLoan.getPaymentAmount()).thenReturn(Money.of(5));
+ when(secondLoan.getPaymentAmount()).thenReturn(Money.of(5));
+ }
+
+ /**
+ * Initializes the loans with the specified number of days till the next payment.
+ *
+ * @param daysTillFirstLoan The number of days till the next payment for the first loan.
+ * @param daysTillSecondLoan The number of days till the next payment for the second loan.
+ */
+ private void initializeLoans(int daysTillFirstLoan, int daysTillSecondLoan) {
+ when(finances.getLoans()).thenReturn(List.of(firstLoan, secondLoan));
+
+ when(firstLoan.getNextPayment()).thenReturn(today.plusDays(daysTillFirstLoan));
+ when(secondLoan.getNextPayment()).thenReturn(today.plusDays(daysTillSecondLoan));
+ }
+
+ // In the following tests the getTotalPaymentsDue() method is called, and its response
+ // is checked against expected behavior
+
+ @Test
+ void noLoans() {
+ when(finances.getLoans()).thenReturn(new ArrayList<>());
+
+ assertEquals(Money.zero(), getTotalPaymentsDue(campaign));
+ }
+
+ @Test
+ void noLoanDueTomorrow() {
+ initializeLoans(2, 2);
+
+ assertEquals(Money.zero(), getTotalPaymentsDue(campaign));
+ }
+
+ @Test
+ void oneLoanDueTomorrow() {
+ initializeLoans(2, 1);
+
+ assertEquals(Money.of(5), getTotalPaymentsDue(campaign));
+ }
+
+ @Test
+ void twoLoansDueTomorrow() {
+ initializeLoans(1, 1);
+
+ assertEquals(Money.of(10), getTotalPaymentsDue(campaign));
+ }
+
+ // In the following tests the canAffordLoans() method is called, and its response is checked
+ // against expected behavior
+
+ @Test
+ void canAffordLoans() {
+ initializeLoans(2, 1);
+
+ when(campaign.getFunds()).thenReturn(Money.of(10));
+
+ assertFalse(isUnableToAffordLoanPayment(campaign));
+ }
+
+ @Test
+ void cannotAffordLoans() {
+ initializeLoans(1, 1);
+
+ when(campaign.getFunds()).thenReturn(Money.of(5));
+
+ assertTrue(isUnableToAffordLoanPayment(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialogTest.java
new file mode 100644
index 0000000000..63b2911e6e
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnmaintainedUnitsNagDialogTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.Hangar;
+import mekhq.campaign.unit.Unit;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static mekhq.gui.dialog.nagDialogs.UnmaintainedUnitsNagDialog.checkHanger;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link UnmaintainedUnitsNagDialog} class.
+ * It tests the different combinations of unit states and verifies the behavior of the {@code checkHanger()} method.
+ */
+class UnmaintainedUnitsNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+ private Hangar hangar;
+ private Unit mockUnit1, mockUnit2;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ hangar = mock(Hangar.class);
+ mockUnit1 = mock(Unit.class);
+ mockUnit2 = mock(Unit.class);
+
+ // When the Campaign mock calls 'getHangar()' return the 'hangar' mock
+ when(campaign.getHangar()).thenReturn(hangar);
+ }
+
+ /**
+ * Initializes the units by setting their maintenance status and salvage status.
+ *
+ * @param unit1Unmaintained A boolean indicating whether the first unit is unmaintained.
+ * @param unit1Salvage A boolean indicating whether the first unit is salvage.
+ * @param unit2Unmaintained A boolean indicating whether the second unit is unmaintained.
+ * @param unit2Salvage A boolean indicating whether the second unit is salvage.
+ */
+ private void initializeUnits(boolean unit1Unmaintained, boolean unit1Salvage, boolean unit2Unmaintained, boolean unit2Salvage) {
+ when(mockUnit1.isUnmaintained()).thenReturn(unit1Unmaintained);
+ when(mockUnit1.isSalvage()).thenReturn(unit1Salvage);
+
+ when(mockUnit2.isUnmaintained()).thenReturn(unit2Unmaintained);
+ when(mockUnit2.isSalvage()).thenReturn(unit2Salvage);
+
+ List units = List.of(mockUnit1, mockUnit2);
+ when(hangar.getUnits()).thenReturn(units);
+ }
+
+ // In the following tests the checkHanger() method is called, and its response is checked
+ // against expected behavior
+
+ @Test
+ void unmaintainedUnitExistsUnit1() {
+ initializeUnits(true, false, false, false);
+ assertTrue(checkHanger(campaign));
+ }
+
+ @Test
+ void unmaintainedUnitExistsUnit2() {
+ initializeUnits(false, false, true, false);
+ assertTrue(checkHanger(campaign));
+ }
+
+ @Test
+ void unmaintainedUnitExistsButSalvageUnit1() {
+ initializeUnits(true, true, true, false);
+ assertTrue(checkHanger(campaign));
+ }
+
+ @Test
+ void unmaintainedUnitExistsButSalvageUnit2() {
+ initializeUnits(true, false, true, true);
+ assertTrue(checkHanger(campaign));
+ }
+
+ @Test
+ void unmaintainedUnitExistsButSalvageMixed() {
+ initializeUnits(false, true, true, false);
+ assertTrue(checkHanger(campaign));
+ }
+
+ @Test
+ void noUnmaintainedUnitExistsNoSalvage() {
+ initializeUnits(false, false, false, false);
+ assertFalse(checkHanger(campaign));
+ }
+
+ @Test
+ void noUnmaintainedUnitExistsAllSalvage() {
+ initializeUnits(false, true, false, true);
+ assertFalse(checkHanger(campaign));
+ }
+
+ @Test
+ void noUnmaintainedUnitExistsButSalvageUnit1() {
+ initializeUnits(false, true, false, false);
+ assertFalse(checkHanger(campaign));
+ }
+
+ @Test
+ void noUnmaintainedUnitExistsButSalvageUnit2() {
+ initializeUnits(false, false, false, true);
+ assertFalse(checkHanger(campaign));
+ }
+
+ @Test
+ void noUnmaintainedUnitExistsButSalvageMixed() {
+ initializeUnits(false, true, false, false);
+ assertFalse(checkHanger(campaign));
+ }
+}
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialogTest.java
new file mode 100644
index 0000000000..b62254caf8
--- /dev/null
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnresolvedStratConContactsNagDialogTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.nagDialogs;
+
+import mekhq.campaign.Campaign;
+import mekhq.campaign.CampaignOptions;
+import mekhq.campaign.mission.AtBContract;
+import mekhq.campaign.stratcon.StratconCampaignState;
+import mekhq.campaign.stratcon.StratconCoords;
+import mekhq.campaign.stratcon.StratconScenario;
+import mekhq.campaign.stratcon.StratconScenario.ScenarioState;
+import mekhq.campaign.stratcon.StratconTrackState;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.util.*;
+
+import static mekhq.gui.dialog.nagDialogs.UnresolvedStratConContactsNagDialog.nagUnresolvedContacts;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This class is a test class for the {@link UnresolvedStratConContactsNagDialog} class.
+ * It contains tests for various scenarios related to the {@code nagUnresolvedContacts} method
+ */
+public class UnresolvedStratConContactsNagDialogTest {
+ // Mock objects for the tests
+ private Campaign campaign;
+ private LocalDate today;
+ private CampaignOptions options;
+ private AtBContract contract;
+ private StratconCampaignState stratconCampaignState;
+ private StratconTrackState track;
+
+ private StratconCoords coordinates;
+ private StratconScenario scenarioNotDue, scenarioDue;
+
+ /**
+ * Test setup for each test, runs before each test.
+ * Initializes the mock objects and sets up the necessary mock behaviors.
+ */
+ @BeforeEach
+ void init() {
+ // Initialize the mock objects
+ campaign = mock(Campaign.class);
+ options = mock(CampaignOptions.class);
+ today = LocalDate.now();
+
+ contract = mock(AtBContract.class);
+
+ stratconCampaignState = mock(StratconCampaignState.class);
+ track = mock(StratconTrackState.class);
+
+ coordinates = mock(StratconCoords.class);
+
+ scenarioNotDue = mock(StratconScenario.class);
+ scenarioDue = mock(StratconScenario.class);
+
+ // Stubs
+ when(campaign.getCampaignOptions()).thenReturn(options);
+ when(campaign.getLocalDate()).thenReturn(today);
+
+ when(contract.getStratconCampaignState()).thenReturn(stratconCampaignState);
+ when(stratconCampaignState.getTracks()).thenReturn(Collections.singletonList(track));
+
+ when(options.isUseStratCon()).thenReturn(true);
+ when(campaign.getActiveAtBContracts()).thenReturn(List.of(contract));
+
+ when(scenarioDue.getCurrentState()).thenReturn(ScenarioState.UNRESOLVED);
+ when(scenarioDue.getDeploymentDate()).thenReturn(today);
+ when(scenarioDue.getName()).thenReturn("Scenario Due");
+
+ when(scenarioNotDue.getCurrentState()).thenReturn(ScenarioState.UNRESOLVED);
+ when(scenarioNotDue.getDeploymentDate()).thenReturn(today.plusDays(1));
+ when(scenarioNotDue.getName()).thenReturn("Scenario Not Due");
+ }
+
+ @Test
+ void stratConDisabled() {
+ when(options.isUseStratCon()).thenReturn(false);
+
+ assertEquals("", nagUnresolvedContacts(campaign));
+ }
+
+ @Test
+ void noActiveContract() {
+ when(campaign.getActiveAtBContracts()).thenReturn(new ArrayList<>());
+
+ assertEquals("", nagUnresolvedContacts(campaign));
+ }
+
+ @Test
+ void nullStratConState() {
+ when(contract.getStratconCampaignState()).thenReturn(null);
+
+ assertEquals("", nagUnresolvedContacts(campaign));
+ }
+
+ @Test
+ void noScenarios() {
+ when(track.getScenarios()).thenReturn(new HashMap<>());
+
+ assertEquals("", nagUnresolvedContacts(campaign));
+ }
+
+ @Test
+ void noUnresolvedScenarios() {
+ Map mockMap = new HashMap<>();
+ mockMap.put(coordinates, scenarioNotDue);
+
+ when(track.getScenarios()).thenReturn(mockMap);
+ when(scenarioNotDue.getCurrentState()).thenReturn(ScenarioState.COMPLETED);
+
+ assertEquals("", nagUnresolvedContacts(campaign));
+ }
+
+ @Test
+ void noScenariosDue() {
+ when(track.getScenarios()).thenReturn(Collections.singletonMap(coordinates, scenarioNotDue));
+ when(track.getDisplayableName()).thenReturn("Test Track");
+
+ assertEquals("", nagUnresolvedContacts(campaign));
+ }
+
+ @Test
+ void scenarioDue() {
+ when(track.getScenarios()).thenReturn(Collections.singletonMap(coordinates, scenarioDue));
+ when(track.getDisplayableName()).thenReturn("Test Track");
+
+ assertEquals("Scenario Due, Test Track\n", nagUnresolvedContacts(campaign));
+ }
+}
\ No newline at end of file
diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UntreatedPersonnelNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UntreatedPersonnelNagDialogTest.java
index 6d53db7cb9..f27513b2f1 100644
--- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UntreatedPersonnelNagDialogTest.java
+++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UntreatedPersonnelNagDialogTest.java
@@ -1,6 +1,25 @@
+/*
+ * 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.nagDialogs;
import megamek.common.EquipmentType;
+import megamek.logging.MMLogger;
import mekhq.campaign.Campaign;
import mekhq.campaign.personnel.Person;
import mekhq.campaign.personnel.SkillType;
@@ -9,7 +28,6 @@
import mekhq.campaign.personnel.enums.PrisonerStatus;
import mekhq.campaign.personnel.ranks.Ranks;
import mekhq.campaign.universe.Systems;
-import org.apache.logging.log4j.LogManager;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -18,10 +36,18 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
+/**
+ * This class contains test methods for the {@link UntreatedPersonnelNagDialog} class.
+ * It tests the different combinations of untreated personnel and verifies the behavior of the
+ * {@code isUntreatedInjury()} method.
+ */
class UntreatedPersonnelNagDialogTest {
Campaign campaign;
Person person;
+ /**
+ * Sets up the necessary dependencies and configurations before running the test methods.
+ */
@BeforeAll
public static void setup() {
EquipmentType.initializeTypes();
@@ -30,10 +56,14 @@ public static void setup() {
try {
Systems.setInstance(Systems.loadDefault());
} catch (Exception ex) {
- LogManager.getLogger().error("", ex);
+ MMLogger.create(UntreatedPersonnelNagDialogTest.class).error("", ex);
}
}
+ /**
+ * Initializes the campaign and creates a new person with the specified personnel role.
+ * This person is assigned one hit.
+ */
@BeforeEach
public void init() {
campaign = new Campaign();
@@ -41,6 +71,9 @@ public void init() {
person.setHits(1);
}
+ // In the following tests the isUntreatedInjury() method is called, and its response is checked
+ // against expected behavior
+
@Test
public void isUntreatedInjuryIncludesNonPrisonersTest() {
person.setPrisonerStatus(campaign, PrisonerStatus.FREE, false);
diff --git a/MekHQ/userdata/data/universe/ranks.xml b/MekHQ/userdata/data/universe/ranks.xml
index a84279a626..9222a5a93e 100644
--- a/MekHQ/userdata/data/universe/ranks.xml
+++ b/MekHQ/userdata/data/universe/ranks.xml
@@ -1,3 +1,3 @@
-
+