From d754108eb9b2940bdd047e19faf5844aa77fc469 Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 7 Oct 2024 17:23:56 -0700 Subject: [PATCH 001/118] Architecture and options for colored skill levels --- .../resources/mekhq/resources/GUI.properties | 5 ++ MekHQ/src/mekhq/MHQConstants.java | 5 ++ MekHQ/src/mekhq/MHQOptions.java | 82 +++++++++++++++++++ .../mekhq/campaign/personnel/SkillType.java | 47 +++++++++++ .../mekhq/gui/dialog/MHQOptionsDialog.java | 47 ++++++++++- 5 files changed, 184 insertions(+), 2 deletions(-) diff --git a/MekHQ/resources/mekhq/resources/GUI.properties b/MekHQ/resources/mekhq/resources/GUI.properties index e3dae29a9d..64b2f4010f 100644 --- a/MekHQ/resources/mekhq/resources/GUI.properties +++ b/MekHQ/resources/mekhq/resources/GUI.properties @@ -702,6 +702,11 @@ optionStratConHexCoordForeground.text=StratCon Hex Coordinate Foreground optionFontColorNegative.text=Font Color Negative Event optionFontColorWarning.text=Font Color Warning optionFontColorPositive.text=Font Color Positive Event +optionFontColorSkillUltraGreen.text=Font Color Ultra Green Skill +optionFontColorSkillGreen.text=Font Color Green Skill +optionFontColorSkillRegular.text=Font Color Regular Skill +optionFontColorSkillVeteran.text=Font Color Veteran Skill +optionFontColorSkillElite.text=Font Color Elite Skill ## Fonts Tab fontsTab.title=Fonts diff --git a/MekHQ/src/mekhq/MHQConstants.java b/MekHQ/src/mekhq/MHQConstants.java index 717c3a1bb1..a115e3dd55 100644 --- a/MekHQ/src/mekhq/MHQConstants.java +++ b/MekHQ/src/mekhq/MHQConstants.java @@ -118,6 +118,11 @@ public final class MHQConstants extends SuiteConstants { public static final String FONT_COLOR_NEGATIVE = "fontColorNegative"; public static final String FONT_COLOR_POSITIVE = "fontColorPositive"; public static final String FONT_COLOR_WARNING = "fontColorWarning"; + public static final String FONT_COLOR_SKILL_ULTRAGREEN = "fontColorSkillUltraGreen"; + public static final String FONT_COLOR_SKILL_GREEN = "fontColorSkillGreen"; + public static final String FONT_COLOR_SKILL_REGULAR = "fontColorSkillRegular"; + public static final String FONT_COLOR_SKILL_VETERAN = "fontColorSkillVeteran"; + public static final String FONT_COLOR_SKILL_ELITE = "fontColorSkillElite"; // endregion Colours // region Fonts diff --git a/MekHQ/src/mekhq/MHQOptions.java b/MekHQ/src/mekhq/MHQOptions.java index b07659c907..6640543be2 100644 --- a/MekHQ/src/mekhq/MHQOptions.java +++ b/MekHQ/src/mekhq/MHQOptions.java @@ -546,6 +546,88 @@ public void setFontColorWarning(Color value) { userPreferences.node(MHQConstants.DISPLAY_NODE).putInt(MHQConstants.FONT_COLOR_WARNING, value.getRGB()); } + + public Color getFontColorSkillUltraGreen() { + return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_ULTRAGREEN, + 0x33ff33)); + } + + /** + * @return the hexadecimal color code for the ultra green skill. + */ + public String getFontColorSkillUltraGreenHexColor() { + return convertFontColorToHexColor(getFontColorSkillUltraGreen()); + } + + public void setFontColorSkillUltraGreen(Color value) { + userPreferences.node(MHQConstants.DISPLAY_NODE).putInt(MHQConstants.FONT_COLOR_SKILL_ULTRAGREEN, value.getRGB()); + } + + public Color getFontColorSkillGreen() { + return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_GREEN, + 0x00ff00)); + } + + /** + * @return the hexadecimal color code for the green skill. + */ + public String getFontColorSkillGreenHexColor() { + return convertFontColorToHexColor(getFontColorSkillGreen()); + } + + public void setFontColorSkillGreen(Color value) { + userPreferences.node(MHQConstants.DISPLAY_NODE).putInt(MHQConstants.FONT_COLOR_SKILL_GREEN, value.getRGB()); + } + + public Color getFontColorSkillRegular() { + return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_REGULAR, + 0x997755)); + } + + /** + * @return the hexadecimal color code for the regular skill. + */ + public String getFontColorSkillRegularHexColor() { + return convertFontColorToHexColor(getFontColorSkillRegular()); + } + + public void setFontColorSkillRegular(Color value) { + userPreferences.node(MHQConstants.DISPLAY_NODE).putInt(MHQConstants.FONT_COLOR_SKILL_REGULAR, value.getRGB()); + } + + public Color getFontColorSkillVeteran() { + return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_VETERAN, + 0xff0000)); + } + + /** + * @return the hexadecimal color code for the veteran skill. + */ + public String getFontColorSkillVeteranHexColor() { + return convertFontColorToHexColor(getFontColorSkillVeteran()); + } + + public void setFontColorSkillVeteran(Color value) { + userPreferences.node(MHQConstants.DISPLAY_NODE).putInt(MHQConstants.FONT_COLOR_SKILL_VETERAN, value.getRGB()); + } + + public Color getFontColorSkillElite() { + return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_ELITE, + 0xdd33dd)); + } + + /** + * @return the hexadecimal color code for the elite skill. + */ + public String getFontColorSkillEliteHexColor() { + return convertFontColorToHexColor(getFontColorSkillElite()); + } + + public void setFontColorSkillElite(Color value) { + userPreferences.node(MHQConstants.DISPLAY_NODE).putInt(MHQConstants.FONT_COLOR_SKILL_ELITE, value.getRGB()); + } + + /** * Converts the font color to a hexadecimal color representation. * diff --git a/MekHQ/src/mekhq/campaign/personnel/SkillType.java b/MekHQ/src/mekhq/campaign/personnel/SkillType.java index 1683915839..07cea4e96c 100644 --- a/MekHQ/src/mekhq/campaign/personnel/SkillType.java +++ b/MekHQ/src/mekhq/campaign/personnel/SkillType.java @@ -22,6 +22,7 @@ import megamek.common.*; import megamek.logging.MMLogger; +import mekhq.MekHQ; import mekhq.utilities.MHQXMLUtility; import org.apache.commons.lang3.StringUtils; import org.w3c.dom.Node; @@ -125,6 +126,10 @@ public class SkillType { public static final int EXP_VETERAN = 3; public static final int EXP_ELITE = 4; + /** + * @param level - skill level to get name of + * @return String skill name + */ public static String getExperienceLevelName(int level) { switch (level) { case EXP_ULTRA_GREEN: @@ -144,6 +149,48 @@ public static String getExperienceLevelName(int level) { } } + /** + * @param level - skill level to get color for + * @return String hex code for a font tag + */ + + public static String getExperienceLevelColor(int level) { + switch (level) { + case EXP_ULTRA_GREEN: + return MekHQ.getMHQOptions().getFontColorSkillUltraGreenHexColor(); + case EXP_GREEN: + return MekHQ.getMHQOptions().getFontColorSkillGreenHexColor(); + case EXP_REGULAR: + return MekHQ.getMHQOptions().getFontColorSkillRegularHexColor(); + case EXP_VETERAN: + return MekHQ.getMHQOptions().getFontColorSkillVeteranHexColor(); + case EXP_ELITE: + return MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); + case -1: + return ""; + default: + return ""; + } + } + + /** + * @param level - skill level to get tagged name for + * @return String "SkillName" or "Skillname" if no color exists + */ + public static String getColoredExperienceLevelName(int level) { + if (getExperienceLevelColor(level) == "") { + return getExperienceLevelName(level); + } + + StringBuilder toReturn = new StringBuilder(64); + toReturn.append(""); + toReturn.append(getExperienceLevelName(level)); + toReturn.append(""); + return toReturn.toString(); + } + private String name; private int target; private boolean countUp; diff --git a/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java b/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java index b9bdbabd4d..8886539028 100644 --- a/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java @@ -130,6 +130,11 @@ public class MHQOptionsDialog extends AbstractMHQButtonDialog { private ColourSelectorButton optionFontColorNegative; private ColourSelectorButton optionFontColorWarning; private ColourSelectorButton optionFontColorPositive; + private ColourSelectorButton optionFontColorSkillUltraGreen; + private ColourSelectorButton optionFontColorSkillGreen; + private ColourSelectorButton optionFontColorSkillRegular; + private ColourSelectorButton optionFontColorSkillVeteran; + private ColourSelectorButton optionFontColorSkillElite; // endregion Colors // region Fonts @@ -575,6 +580,17 @@ private JPanel createColoursTab() { optionFontColorWarning = new ColourSelectorButton(resources.getString("optionFontColorWarning.text")); optionFontColorPositive = new ColourSelectorButton(resources.getString("optionFontColorPositive.text")); + + optionFontColorSkillUltraGreen = new ColourSelectorButton(resources.getString("optionFontColorSkillUltraGreen.text")); + + optionFontColorSkillGreen = new ColourSelectorButton(resources.getString("optionFontColorSkillGreen.text")); + + optionFontColorSkillRegular = new ColourSelectorButton(resources.getString("optionFontColorSkillRegular.text")); + + optionFontColorSkillVeteran = new ColourSelectorButton(resources.getString("optionFontColorSkillVeteran.text")); + + optionFontColorSkillElite = new ColourSelectorButton(resources.getString("optionFontColorSkillElite.text")); + // endregion Create Graphical Components // region Layout @@ -655,7 +671,17 @@ private JPanel createColoursTab() { .addComponent(optionFontColorPositive, Alignment.TRAILING)) .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(optionFontColorWarning))); + .addComponent(optionFontColorWarning) + .addComponent(optionFontColorSkillUltraGreen, + Alignment.TRAILING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(optionFontColorSkillGreen) + .addComponent(optionFontColorSkillRegular, + Alignment.TRAILING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(optionFontColorSkillVeteran) + .addComponent(optionFontColorSkillElite, + Alignment.TRAILING))); layout.setHorizontalGroup( layout.createParallelGroup(Alignment.LEADING) @@ -710,7 +736,14 @@ private JPanel createColoursTab() { .addComponent(optionFontColorNegative) .addComponent(optionFontColorPositive)) .addGroup(layout.createSequentialGroup() - .addComponent(optionFontColorWarning))); + .addComponent(optionFontColorWarning) + .addComponent(optionFontColorSkillUltraGreen)) + .addGroup(layout.createSequentialGroup() + .addComponent(optionFontColorSkillGreen) + .addComponent(optionFontColorSkillRegular)) + .addGroup(layout.createSequentialGroup() + .addComponent(optionFontColorSkillVeteran) + .addComponent(optionFontColorSkillElite))); // endregion Layout return body; @@ -1330,6 +1363,11 @@ protected void okAction() { MekHQ.getMHQOptions().setFontColorNegative(optionFontColorNegative.getColour()); MekHQ.getMHQOptions().setFontColorWarning(optionFontColorWarning.getColour()); MekHQ.getMHQOptions().setFontColorPositive(optionFontColorPositive.getColour()); + MekHQ.getMHQOptions().setFontColorSkillUltraGreen(optionFontColorSkillUltraGreen.getColour()); + MekHQ.getMHQOptions().setFontColorSkillGreen(optionFontColorSkillGreen.getColour()); + MekHQ.getMHQOptions().setFontColorSkillRegular(optionFontColorSkillRegular.getColour()); + MekHQ.getMHQOptions().setFontColorSkillVeteran(optionFontColorSkillVeteran.getColour()); + MekHQ.getMHQOptions().setFontColorSkillElite(optionFontColorSkillElite.getColour()); MekHQ.getMHQOptions().setMedicalViewDialogHandwritingFont( comboMedicalViewDialogHandwritingFont.getFont().getFamily()); @@ -1476,6 +1514,11 @@ private void setInitialState() { optionFontColorNegative.setColour(MekHQ.getMHQOptions().getFontColorNegative()); optionFontColorWarning.setColour(MekHQ.getMHQOptions().getFontColorWarning()); optionFontColorPositive.setColour(MekHQ.getMHQOptions().getFontColorPositive()); + optionFontColorSkillUltraGreen.setColour(MekHQ.getMHQOptions().getFontColorSkillUltraGreen()); + optionFontColorSkillGreen.setColour(MekHQ.getMHQOptions().getFontColorSkillGreen()); + optionFontColorSkillRegular.setColour(MekHQ.getMHQOptions().getFontColorSkillRegular()); + optionFontColorSkillVeteran.setColour(MekHQ.getMHQOptions().getFontColorSkillVeteran()); + optionFontColorSkillElite.setColour(MekHQ.getMHQOptions().getFontColorSkillElite()); comboMedicalViewDialogHandwritingFont.setSelectedItem( new FontDisplay(MekHQ.getMHQOptions().getMedicalViewDialogHandwritingFont())); From 777c5e77594d1838603f2085b1b484e186724df1 Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 7 Oct 2024 17:24:58 -0700 Subject: [PATCH 002/118] Colored Skill Levels for Tech Table --- MekHQ/src/mekhq/gui/model/TechTableModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/gui/model/TechTableModel.java b/MekHQ/src/mekhq/gui/model/TechTableModel.java index ebce04d646..dbf076a01f 100644 --- a/MekHQ/src/mekhq/gui/model/TechTableModel.java +++ b/MekHQ/src/mekhq/gui/model/TechTableModel.java @@ -120,7 +120,7 @@ public String getTechDesc(Person tech, boolean overtimeAllowed, IPartWork part) toReturn.append("; "); } - toReturn.append(SkillType.getExperienceLevelName(skill.getExperienceLevel())); + toReturn.append(SkillType.getColoredExperienceLevelName(skill.getExperienceLevel())); toReturn.append(' ').append(skillName); first = false; } From f2a39b64fc1afae8a22bdba572113a175042b092 Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 7 Oct 2024 17:54:21 -0700 Subject: [PATCH 003/118] Colored skill levels for personnel market --- MekHQ/src/mekhq/campaign/market/PersonnelMarket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java b/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java index c44c613622..c328a0c2ab 100644 --- a/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java +++ b/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java @@ -157,7 +157,7 @@ public void generatePersonnelForDay(Campaign campaign) { stringBuilder.append("
A "); } stringBuilder.append(" ") - .append(expLevel) + .append(SkillType.getColoredExperienceLevelName(person.getExperienceLevel(campaign, false))) .append(' ') .append(person.getPrimaryRole().toString()) .append("") From c3456a5ca4c7695b909c58ebf98d20c7069e9615 Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 7 Oct 2024 20:15:23 -0700 Subject: [PATCH 004/118] More skill level colors architecture --- .../mekhq/campaign/personnel/SkillType.java | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/SkillType.java b/MekHQ/src/mekhq/campaign/personnel/SkillType.java index 07cea4e96c..ac47969db0 100644 --- a/MekHQ/src/mekhq/campaign/personnel/SkillType.java +++ b/MekHQ/src/mekhq/campaign/personnel/SkillType.java @@ -22,6 +22,7 @@ import megamek.common.*; import megamek.logging.MMLogger; +import megamek.common.enums.*; import mekhq.MekHQ; import mekhq.utilities.MHQXMLUtility; import org.apache.commons.lang3.StringUtils; @@ -153,7 +154,6 @@ public static String getExperienceLevelName(int level) { * @param level - skill level to get color for * @return String hex code for a font tag */ - public static String getExperienceLevelColor(int level) { switch (level) { case EXP_ULTRA_GREEN: @@ -173,6 +173,31 @@ public static String getExperienceLevelColor(int level) { } } + /** + * @param level - SkillLevel to get color for + * @return String hex code for a font tag + */ + public static String getExperienceLevelColor(SkillLevel level) { + switch(level) { + case ULTRA_GREEN: + return MekHQ.getMHQOptions().getFontColorSkillUltraGreenHexColor(); + case GREEN: + return MekHQ.getMHQOptions().getFontColorSkillGreenHexColor(); + case REGULAR: + return MekHQ.getMHQOptions().getFontColorSkillRegularHexColor(); + case VETERAN: + return MekHQ.getMHQOptions().getFontColorSkillVeteranHexColor(); + case ELITE: + return MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); + case HEROIC: + return MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); + case LEGENDARY: + return MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); + default: + return ""; + } + } + /** * @param level - skill level to get tagged name for * @return String "SkillName" or "Skillname" if no color exists @@ -191,6 +216,30 @@ public static String getColoredExperienceLevelName(int level) { return toReturn.toString(); } + /** + * @param level - SkillLevel to get tagged name for + * @return String "SkillName" or "Skillname" if no color exists + */ + public static String getColoredExperienceLevelName(SkillLevel level) { + logger.info(level); + logger.info(level.toString()); + logger.info(getExperienceLevelColor(level)); + if (getExperienceLevelColor(level) == "") { + return level.toString(); + } + + StringBuilder toReturn = new StringBuilder(64); + toReturn.append(""); + toReturn.append(level.toString()); + toReturn.append(""); + return toReturn.toString(); + } + + + + private String name; private int target; private boolean countUp; From 3e4f1beeef4162ea4a53a6cfd84043a6690048ca Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 7 Oct 2024 20:15:53 -0700 Subject: [PATCH 005/118] Colored skill levels for experience rating on command center --- MekHQ/src/mekhq/gui/CommandCenterTab.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/gui/CommandCenterTab.java b/MekHQ/src/mekhq/gui/CommandCenterTab.java index ab303b19bf..7b1e75a07e 100644 --- a/MekHQ/src/mekhq/gui/CommandCenterTab.java +++ b/MekHQ/src/mekhq/gui/CommandCenterTab.java @@ -229,7 +229,12 @@ private void initInfoPanel() { if (getCampaign().getCampaignOptions().getUnitRatingMethod().isFMMR()) { lblExperience.setText(getCampaign().getUnitRating().getAverageExperience().toString()); } else { - lblExperience.setText(getCampaign().getReputation().getAverageSkillLevel().toString()); + // This seems to be overwritten completely and immediately by refresh + StringBuilder experienceString = new StringBuilder(64); + experienceString.append("") + .append(mekhq.campaign.personnel.SkillType.getColoredExperienceLevelName(getCampaign().getReputation().getAverageSkillLevel())) + .append(""); + lblExperience.setText(experienceString.toString()); } lblExperienceHead.setLabelFor(lblExperience); @@ -582,7 +587,12 @@ private void refreshBasicInfo() { reputationController.initializeReputation(campaign); campaign.setReputation(reputationController); } - lblExperience.setText(campaign.getReputation().getAverageSkillLevel().toString()); + + StringBuilder experienceString = new StringBuilder(64); + experienceString.append("") + .append(mekhq.campaign.personnel.SkillType.getColoredExperienceLevelName(campaign.getReputation().getAverageSkillLevel())) + .append(""); + lblExperience.setText(experienceString.toString()); } campaignSummary.updateInformation(); From 801c1bf1b9591020f5f2265487ffcceaaa1f96a8 Mon Sep 17 00:00:00 2001 From: Weaver Date: Tue, 8 Oct 2024 12:26:56 -0700 Subject: [PATCH 006/118] Update skill color architecture to better style --- .../mekhq/campaign/personnel/SkillType.java | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/SkillType.java b/MekHQ/src/mekhq/campaign/personnel/SkillType.java index ac47969db0..1238b88543 100644 --- a/MekHQ/src/mekhq/campaign/personnel/SkillType.java +++ b/MekHQ/src/mekhq/campaign/personnel/SkillType.java @@ -128,7 +128,7 @@ public class SkillType { public static final int EXP_ELITE = 4; /** - * @param level - skill level to get name of + * @param level skill level integer to get name for * @return String skill name */ public static String getExperienceLevelName(int level) { @@ -151,7 +151,7 @@ public static String getExperienceLevelName(int level) { } /** - * @param level - skill level to get color for + * @param level skill level integer to get color for * @return String hex code for a font tag */ public static String getExperienceLevelColor(int level) { @@ -174,7 +174,7 @@ public static String getExperienceLevelColor(int level) { } /** - * @param level - SkillLevel to get color for + * @param level SkillLevel enum to get color for * @return String hex code for a font tag */ public static String getExperienceLevelColor(SkillLevel level) { @@ -199,7 +199,7 @@ public static String getExperienceLevelColor(SkillLevel level) { } /** - * @param level - skill level to get tagged name for + * @param level - skill level integer to get tagged name for * @return String "SkillName" or "Skillname" if no color exists */ public static String getColoredExperienceLevelName(int level) { @@ -208,32 +208,29 @@ public static String getColoredExperienceLevelName(int level) { } StringBuilder toReturn = new StringBuilder(64); - toReturn.append(""); - toReturn.append(getExperienceLevelName(level)); - toReturn.append(""); + toReturn.append("") + .append(getExperienceLevelName(level)) + .append(""); return toReturn.toString(); } /** - * @param level - SkillLevel to get tagged name for + * @param level - SkillLevel enum to get tagged name for * @return String "SkillName" or "Skillname" if no color exists */ public static String getColoredExperienceLevelName(SkillLevel level) { - logger.info(level); - logger.info(level.toString()); - logger.info(getExperienceLevelColor(level)); if (getExperienceLevelColor(level) == "") { return level.toString(); } StringBuilder toReturn = new StringBuilder(64); - toReturn.append(""); - toReturn.append(level.toString()); - toReturn.append(""); + toReturn.append("") + .append(level.toString()) + .append(""); return toReturn.toString(); } From baaf69af94a37f96639277a5ff1ab3c7c0eb45a6 Mon Sep 17 00:00:00 2001 From: Weaver Date: Tue, 8 Oct 2024 12:27:21 -0700 Subject: [PATCH 007/118] Colored skill levels for infirmary tab --- MekHQ/src/mekhq/gui/model/DocTableModel.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/gui/model/DocTableModel.java b/MekHQ/src/mekhq/gui/model/DocTableModel.java index 69dbb30f8e..2526b0f8ba 100644 --- a/MekHQ/src/mekhq/gui/model/DocTableModel.java +++ b/MekHQ/src/mekhq/gui/model/DocTableModel.java @@ -54,8 +54,9 @@ private String getDocDesc(Person doc) { Skill skill = doc.getSkill(SkillType.S_DOCTOR); if (null != skill) { - toReturn.append(SkillType.getExperienceLevelName(skill.getExperienceLevel())) - .append(" " + SkillType.S_DOCTOR); + toReturn.append("") + .append(SkillType.getColoredExperienceLevelName(skill.getExperienceLevel())) + .append(" " + SkillType.S_DOCTOR); } toReturn.append(String.format(" (%d XP)", doc.getXP())); From 6a6100b431c7db32ddef1d0a27f60edba6194ee4 Mon Sep 17 00:00:00 2001 From: Weaver Date: Tue, 8 Oct 2024 12:47:07 -0700 Subject: [PATCH 008/118] Bold skill names on techs and CC for better color --- MekHQ/src/mekhq/gui/CommandCenterTab.java | 8 ++++---- MekHQ/src/mekhq/gui/model/TechTableModel.java | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/MekHQ/src/mekhq/gui/CommandCenterTab.java b/MekHQ/src/mekhq/gui/CommandCenterTab.java index 7b1e75a07e..da5fe5da76 100644 --- a/MekHQ/src/mekhq/gui/CommandCenterTab.java +++ b/MekHQ/src/mekhq/gui/CommandCenterTab.java @@ -231,9 +231,9 @@ private void initInfoPanel() { } else { // This seems to be overwritten completely and immediately by refresh StringBuilder experienceString = new StringBuilder(64); - experienceString.append("") + experienceString.append("") .append(mekhq.campaign.personnel.SkillType.getColoredExperienceLevelName(getCampaign().getReputation().getAverageSkillLevel())) - .append(""); + .append(""); lblExperience.setText(experienceString.toString()); } @@ -589,9 +589,9 @@ private void refreshBasicInfo() { } StringBuilder experienceString = new StringBuilder(64); - experienceString.append("") + experienceString.append("") .append(mekhq.campaign.personnel.SkillType.getColoredExperienceLevelName(campaign.getReputation().getAverageSkillLevel())) - .append(""); + .append(""); lblExperience.setText(experienceString.toString()); } diff --git a/MekHQ/src/mekhq/gui/model/TechTableModel.java b/MekHQ/src/mekhq/gui/model/TechTableModel.java index dbf076a01f..ff7c1aafb2 100644 --- a/MekHQ/src/mekhq/gui/model/TechTableModel.java +++ b/MekHQ/src/mekhq/gui/model/TechTableModel.java @@ -120,8 +120,9 @@ public String getTechDesc(Person tech, boolean overtimeAllowed, IPartWork part) toReturn.append("; "); } - toReturn.append(SkillType.getColoredExperienceLevelName(skill.getExperienceLevel())); - toReturn.append(' ').append(skillName); + toReturn.append("") + .append(SkillType.getColoredExperienceLevelName(skill.getExperienceLevel())) + .append(" ").append(skillName); first = false; } From b9c6528e07616037d9f787e59314f728e9b43e05 Mon Sep 17 00:00:00 2001 From: Weaver Date: Tue, 8 Oct 2024 14:06:23 -0700 Subject: [PATCH 009/118] Colored skill levels for repair/salvage part skill requirement --- MekHQ/src/mekhq/campaign/parts/Armor.java | 2 +- MekHQ/src/mekhq/campaign/parts/MekLocation.java | 2 +- MekHQ/src/mekhq/campaign/parts/MissingPart.java | 2 +- MekHQ/src/mekhq/campaign/parts/Part.java | 4 ++-- MekHQ/src/mekhq/campaign/parts/PodSpace.java | 2 +- MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/Armor.java b/MekHQ/src/mekhq/campaign/parts/Armor.java index cba9d91126..d228ee8574 100644 --- a/MekHQ/src/mekhq/campaign/parts/Armor.java +++ b/MekHQ/src/mekhq/campaign/parts/Armor.java @@ -137,7 +137,7 @@ public String getDesc() { toReturn += "Replace " + getName(); if (!getCampaign().getCampaignOptions().isDestroyByMargin()) { - toReturn += " - " + toReturn += " - " + SkillType.getExperienceLevelName(getSkillMin()) + '+' + "
"; } else { diff --git a/MekHQ/src/mekhq/campaign/parts/MekLocation.java b/MekHQ/src/mekhq/campaign/parts/MekLocation.java index d46abfab84..204c182f89 100644 --- a/MekHQ/src/mekhq/campaign/parts/MekLocation.java +++ b/MekHQ/src/mekhq/campaign/parts/MekLocation.java @@ -883,7 +883,7 @@ public String getDesc() { toReturn += " - Impossible
"; } else { - toReturn += " - " + toReturn += " - " + SkillType.getExperienceLevelName(getSkillMin()) + '+' + "
"; } diff --git a/MekHQ/src/mekhq/campaign/parts/MissingPart.java b/MekHQ/src/mekhq/campaign/parts/MissingPart.java index 937683f959..8e1ff6be23 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingPart.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingPart.java @@ -98,7 +98,7 @@ public String getDesc() { if (getSkillMin() > SkillType.EXP_ELITE) { toReturn += " - Impossible"; } else { - toReturn += " - " + toReturn += " - " + SkillType.getExperienceLevelName(getSkillMin()) + '+' + "
"; } diff --git a/MekHQ/src/mekhq/campaign/parts/Part.java b/MekHQ/src/mekhq/campaign/parts/Part.java index 8217b928a4..e58c8ffe66 100644 --- a/MekHQ/src/mekhq/campaign/parts/Part.java +++ b/MekHQ/src/mekhq/campaign/parts/Part.java @@ -441,7 +441,7 @@ public String getDesc() { toReturn += "" + action + getName(); if (!getCampaign().getCampaignOptions().isDestroyByMargin()) { - toReturn += " - " + toReturn += " - " + SkillType.getExperienceLevelName(getSkillMin()) + '+' + "
"; } else { @@ -477,7 +477,7 @@ public String getRepairDesc() { toReturn += getTimeLeft() + " minutes" + scheduled; if (!getCampaign().getCampaignOptions().isDestroyByMargin()) { - toReturn += ", " + toReturn += ", " + SkillType.getExperienceLevelName(getSkillMin()) + '+' + ReportingUtilities.CLOSING_SPAN_TAG; } diff --git a/MekHQ/src/mekhq/campaign/parts/PodSpace.java b/MekHQ/src/mekhq/campaign/parts/PodSpace.java index 154a2cbde6..44344b0029 100644 --- a/MekHQ/src/mekhq/campaign/parts/PodSpace.java +++ b/MekHQ/src/mekhq/campaign/parts/PodSpace.java @@ -385,7 +385,7 @@ public String getDesc() { if (getSkillMin() > SkillType.EXP_ELITE) { toReturn += " - Impossible
"; } else { - toReturn += " - " + toReturn += " - " + SkillType.getExperienceLevelName(getSkillMin()) + '+' + "
"; } diff --git a/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java b/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java index 001656ce3c..d304989227 100644 --- a/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java +++ b/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java @@ -599,7 +599,7 @@ public String getDesc() { toReturn += " - Impossible"; } else { - toReturn += " - " + toReturn += " - " + SkillType.getExperienceLevelName(getSkillMin()) + '+' + "
"; } From 41e6e5d5847e56f17f3e4aeae13f83cf081301c5 Mon Sep 17 00:00:00 2001 From: Weaver Date: Tue, 8 Oct 2024 16:23:58 -0700 Subject: [PATCH 010/118] Skill colors for the refit tech chooser --- MekHQ/src/mekhq/gui/CampaignGUI.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index a86193246f..49ba57b106 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -1614,10 +1614,21 @@ public void refitUnit(Refit r, boolean selectModelName) { if (getCampaign().isWorkingOnRefit(tech) || tech.isEngineer()) { continue; } - name = tech.getFullName() + ", " + tech.getSkillLevel(getCampaign(), false) + ' ' - + tech.getPrimaryRoleDesc() + " (" - + getCampaign().getTargetFor(r, tech).getValueAsString() + "+), " - + tech.getMinutesLeft() + '/' + tech.getDailyAvailableTechTime() + " minutes"; + StringBuilder nameBuilder = new StringBuilder(128); + nameBuilder.append("") + .append(tech.getFullName()) + .append(", ") + .append(SkillType.getColoredExperienceLevelName(tech.getSkillLevel(getCampaign(), false))) + .append(" ") + .append(tech.getPrimaryRoleDesc()) + .append(" (") + .append(getCampaign().getTargetFor(r, tech).getValueAsString()) + .append("+), ") + .append(tech.getMinutesLeft()) + .append('/') + .append(tech.getDailyAvailableTechTime()) + .append(" minutes"); + name = nameBuilder.toString(); techHash.put(name, tech); if (tech.isRightTechTypeFor(r)) { techList.add(lastRightTech++, name); From 68ed40fec12ac9633583b420ae1e4083fec627c5 Mon Sep 17 00:00:00 2001 From: Weaver Date: Tue, 8 Oct 2024 17:53:06 -0700 Subject: [PATCH 011/118] Skill colors for personnel tab --- MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java b/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java index 8b42f7fd2c..f32382fed8 100644 --- a/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java +++ b/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java @@ -489,7 +489,11 @@ public String getCellValue(final Campaign campaign, final PersonnelMarket person case GENDER: return GenderDescriptors.MALE_FEMALE_OTHER.getDescriptorCapitalized(person.getGender()); case SKILL_LEVEL: - return person.getSkillLevel(campaign, false).toString(); + StringBuilder levelName = new StringBuilder(64); + levelName.append("") + .append(SkillType.getColoredExperienceLevelName(person.getSkillLevel(campaign, false))) + .append(""); + return levelName.toString(); case PERSONNEL_ROLE: return person.getRoleDesc(); case UNIT_ASSIGNMENT: { From 3a710b5df034aab1e756995f0b8e2a7bfdcd4ac5 Mon Sep 17 00:00:00 2001 From: Weaver Date: Tue, 8 Oct 2024 18:10:40 -0700 Subject: [PATCH 012/118] Good default colors for skills --- MekHQ/src/mekhq/MHQOptions.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MekHQ/src/mekhq/MHQOptions.java b/MekHQ/src/mekhq/MHQOptions.java index 6640543be2..6d507a55d8 100644 --- a/MekHQ/src/mekhq/MHQOptions.java +++ b/MekHQ/src/mekhq/MHQOptions.java @@ -549,7 +549,7 @@ public void setFontColorWarning(Color value) { public Color getFontColorSkillUltraGreen() { return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_ULTRAGREEN, - 0x33ff33)); + 0x42DFDF)); } /** @@ -565,7 +565,7 @@ public void setFontColorSkillUltraGreen(Color value) { public Color getFontColorSkillGreen() { return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_GREEN, - 0x00ff00)); + 0x43CF43)); } /** @@ -581,7 +581,7 @@ public void setFontColorSkillGreen(Color value) { public Color getFontColorSkillRegular() { return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_REGULAR, - 0x997755)); + 0xCF9F43)); } /** @@ -597,7 +597,7 @@ public void setFontColorSkillRegular(Color value) { public Color getFontColorSkillVeteran() { return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_VETERAN, - 0xff0000)); + 0xE85C5C)); } /** @@ -613,7 +613,7 @@ public void setFontColorSkillVeteran(Color value) { public Color getFontColorSkillElite() { return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_ELITE, - 0xdd33dd)); + 0xC344C3)); } /** From 4059584688dcbd1167f361d5222cf7d4890e5571 Mon Sep 17 00:00:00 2001 From: Weaver Date: Wed, 9 Oct 2024 17:08:09 -0700 Subject: [PATCH 013/118] Skill colors for TO&E sidepanel + minor cleanup --- MekHQ/src/mekhq/gui/view/ForceViewPanel.java | 33 +++++++++++++++----- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/MekHQ/src/mekhq/gui/view/ForceViewPanel.java b/MekHQ/src/mekhq/gui/view/ForceViewPanel.java index f268cd5710..1c4cd0fe32 100644 --- a/MekHQ/src/mekhq/gui/view/ForceViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/ForceViewPanel.java @@ -26,9 +26,11 @@ import mekhq.campaign.finances.Money; import mekhq.campaign.force.Force; import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.SkillType; import mekhq.campaign.unit.Unit; import mekhq.gui.baseComponents.JScrollablePanel; import mekhq.gui.utilities.MarkdownRenderer; +import mekhq.utilities.ReportingUtilities.*; import javax.swing.*; import java.awt.*; @@ -42,6 +44,7 @@ * @author Jay Lawson (jaylawson39 at yahoo.com) */ public class ForceViewPanel extends JScrollablePanel { + private static final MMLogger logger = MMLogger.create(ForceViewPanel.class); private final Force force; private final Campaign campaign; @@ -458,14 +461,30 @@ private void fillSubUnits() { } public String getForceSummary(Person person, Unit unit) { - String toReturn = "" + person.getFullTitle() + "
"; - toReturn += person.getSkillLevel(campaign, false) + " " + person.getRoleDesc(); - if (null != unit && null != unit.getEntity() - && null != unit.getEntity().getCrew() && unit.getEntity().getCrew().getHits() > 0) { - toReturn += "
" + unit.getEntity().getCrew().getHits() + " hit(s)"; + StringBuilder toReturn = new StringBuilder(); + toReturn.append("") + .append(person.getFullTitle()) + .append("
") + .append(SkillType.getColoredExperienceLevelName(person.getSkillLevel(campaign, false))) + .append(" ") + .append(person.getRoleDesc()); + + if (null != unit && null != unit.getEntity() && null != unit.getEntity().getCrew() + && unit.getEntity().getCrew().getHits() > 0) { + // TODO: Make work for advanced medical system. + toReturn.append(' '); + + StringBuilder hitsMessage = new StringBuilder(16); + hitsMessage.append(unit.getEntity().getCrew().getHits()) + .append(" hit"); + if(unit.getEntity().getCrew().getHits() != 1) { + hitsMessage.append('s'); + } + toReturn.append(messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), hitsMessage.toString())); } - toReturn += "
"; - return toReturn; + toReturn.append("
"); + return toReturn.toString(); } public String getForceSummary(Unit unit) { From fa72c9bb5d934189dcbfe674b65f597f0ef86b65 Mon Sep 17 00:00:00 2001 From: Weaver Date: Wed, 9 Oct 2024 19:41:36 -0700 Subject: [PATCH 014/118] ForceViewPanel - fix a few mistakes --- MekHQ/src/mekhq/gui/view/ForceViewPanel.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/MekHQ/src/mekhq/gui/view/ForceViewPanel.java b/MekHQ/src/mekhq/gui/view/ForceViewPanel.java index 1c4cd0fe32..7026298b03 100644 --- a/MekHQ/src/mekhq/gui/view/ForceViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/ForceViewPanel.java @@ -30,7 +30,6 @@ import mekhq.campaign.unit.Unit; import mekhq.gui.baseComponents.JScrollablePanel; import mekhq.gui.utilities.MarkdownRenderer; -import mekhq.utilities.ReportingUtilities.*; import javax.swing.*; import java.awt.*; @@ -44,7 +43,6 @@ * @author Jay Lawson (jaylawson39 at yahoo.com) */ public class ForceViewPanel extends JScrollablePanel { - private static final MMLogger logger = MMLogger.create(ForceViewPanel.class); private final Force force; private final Campaign campaign; @@ -480,7 +478,7 @@ public String getForceSummary(Person person, Unit unit) { if(unit.getEntity().getCrew().getHits() != 1) { hitsMessage.append('s'); } - toReturn.append(messageSurroundedBySpanWithColor( + toReturn.append(mekhq.utilities.ReportingUtilities.messageSurroundedBySpanWithColor( MekHQ.getMHQOptions().getFontColorNegativeHexColor(), hitsMessage.toString())); } toReturn.append("
"); From 855178dbef35aada9ebac9ab9d43fa8f2b38d391 Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 11 Oct 2024 14:51:17 -0700 Subject: [PATCH 015/118] Fix merge mistake --- MekHQ/src/mekhq/gui/view/ForceViewPanel.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/gui/view/ForceViewPanel.java b/MekHQ/src/mekhq/gui/view/ForceViewPanel.java index 4f5ef213be..0408600f33 100644 --- a/MekHQ/src/mekhq/gui/view/ForceViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/ForceViewPanel.java @@ -471,8 +471,7 @@ public String getForceSummary(Person person, Unit unit) { .append(SkillType.getColoredExperienceLevelName(person.getSkillLevel(campaign, false))) .append(" ") .append(person.getRoleDesc()); - .append(person.getRoleDesc()); - + toReturn.append("
"); boolean isInjured = false; From 5b09dcdbb79b96d3b6144313ae7b866b825e993a Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 14 Oct 2024 10:25:35 -0700 Subject: [PATCH 016/118] Update and add quality-mapping functions --- MekHQ/src/mekhq/campaign/parts/Part.java | 118 +++++++++++++---------- 1 file changed, 65 insertions(+), 53 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/Part.java b/MekHQ/src/mekhq/campaign/parts/Part.java index e84081be09..15389087f9 100644 --- a/MekHQ/src/mekhq/campaign/parts/Part.java +++ b/MekHQ/src/mekhq/campaign/parts/Part.java @@ -202,40 +202,63 @@ public Part(int tonnage, boolean omniPodded, Campaign c) { this.isTeamSalvaging = false; } + + /** + * @param quality internal quality code such as Part.QUALITY_A + * @param reverse are quality names reversed per the campaign option + * @return String letter code for quality + */ public static String getQualityName(int quality, boolean reverse) { - switch (quality) { - case QUALITY_A: - if (reverse) { - return "F"; - } - return "A"; - case QUALITY_B: - if (reverse) { - return "E"; - } - return "B"; - case QUALITY_C: - if (reverse) { - return "D"; - } - return "C"; - case QUALITY_D: - if (reverse) { - return "C"; - } - return "D"; - case QUALITY_E: - if (reverse) { - return "B"; - } - return "E"; - case QUALITY_F: - if (reverse) { - return "A"; - } - return "F"; - default: - return "?"; + if (!reverse) { + return switch(quality) { + case QUALITY_A -> "A"; + case QUALITY_B -> "B"; + case QUALITY_C -> "C"; + case QUALITY_D -> "D"; + case QUALITY_E -> "E"; + case QUALITY_F -> "F"; + default -> "?"; + }; + } else { + return switch(quality) { + case QUALITY_A -> "F"; + case QUALITY_B -> "E"; + case QUALITY_C -> "D"; + case QUALITY_D -> "C"; + case QUALITY_E -> "B"; + case QUALITY_F -> "A"; + default -> "?"; + }; + } + } + + /** + * @param name one-character String from A to F; the item quality code + * @param reverse are quality names reversed per the campaign option + * @return internal quality code such as Part.QUALITY_A + * @throws IllegalArgumentException + */ + public static int getQualityFromName(String name, boolean reverse) { + if (!reverse) { + return switch(name) { + case "A" -> QUALITY_A; + case "B" -> QUALITY_B; + case "C" -> QUALITY_C; + case "D" -> QUALITY_D; + case "E" -> QUALITY_E; + case "F" -> QUALITY_F; + default -> throw new IllegalArgumentException("Expecting one-char string A to F"); + }; + } else { + return switch(name) { + case "F" -> QUALITY_A; + case "E" -> QUALITY_B; + case "D" -> QUALITY_C; + case "C" -> QUALITY_D; + case "B" -> QUALITY_E; + case "A" -> QUALITY_F; + default -> throw new IllegalArgumentException("Expecting one-char string A to F"); + }; } } @@ -979,26 +1002,15 @@ public TargetRoll getAllModsForMaintenance() { */ private TargetRoll getQualityMods(TargetRoll mods, Person tech) { int qualityMod = 0; - switch (quality) { - case QUALITY_A: - qualityMod = 3; - break; - case QUALITY_B: - qualityMod = 2; - break; - case QUALITY_C: - qualityMod = 1; - break; - case QUALITY_D: - qualityMod = 0; - break; - case QUALITY_E: - qualityMod = -1; - break; - case QUALITY_F: - qualityMod = -2; - break; - } + qualityMod = switch (quality) { + case QUALITY_A -> 3; + case QUALITY_B -> 2; + case QUALITY_C -> 1; + case QUALITY_D -> 0; + case QUALITY_E -> -1; + case QUALITY_F -> -2; + default -> 0; // Possibly should be an exception? + }; mods.addModifier(qualityMod, getQualityName(quality, campaign.getCampaignOptions().isReverseQualityNames())); if ((qualityMod > 0) && (null != tech) && From cf393845eaece5b458f1e97f6395e73a417ab8e5 Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 14 Oct 2024 10:37:31 -0700 Subject: [PATCH 017/118] PIU Dialog - Tweak column widths a little --- .../mekhq/gui/model/PartsInUseTableModel.java | 29 ++++++------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java b/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java index 525b2285cf..a8e105d52b 100644 --- a/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java +++ b/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java @@ -199,26 +199,15 @@ public int getAlignment(int column) { } public int getPreferredWidth(int column) { - switch (column) { - case COL_PART: - return 300; - case COL_IN_USE: - case COL_STORED: - case COL_TONNAGE: - case COL_IN_TRANSFER: - case COL_COST: - return 20; - case COL_BUTTON_BUY: - case COL_BUTTON_SELL: - return 50; - case COL_BUTTON_GMADD: - return 70; - case COL_BUTTON_BUY_BULK: - case COL_BUTTON_SELL_BULK: - return 80; - default: - return 100; - } + return switch (column) { + case COL_PART -> 260; + case COL_IN_USE, COL_STORED, COL_TONNAGE, COL_IN_TRANSFER -> 20; + case COL_COST -> 40; + case COL_BUTTON_BUY, COL_BUTTON_SELL -> 25; + case COL_BUTTON_GMADD -> 65; + case COL_BUTTON_BUY_BULK, COL_BUTTON_SELL_BULK -> 65; + default -> 100; + }; } public boolean hasConstantWidth(int col) { From 42183dfea34e0f851b6070d8b7fe4ff1224ff041 Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 14 Oct 2024 10:38:16 -0700 Subject: [PATCH 018/118] Parts in Use - Filter by mothball and quality --- MekHQ/src/mekhq/campaign/Campaign.java | 29 ++++-- .../mekhq/gui/dialog/PartsReportDialog.java | 93 +++++++++++++++---- .../mekhq/gui/model/PartsInUseTableModel.java | 2 +- 3 files changed, 98 insertions(+), 26 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index c14e3e0a16..4367441321 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -2243,13 +2243,21 @@ private PartInUse getPartInUse(Part p) { return (null != result.getPartToBuy()) ? result : null; } - private void updatePartInUseData(PartInUse piu, Part p) { - if ((p.getUnit() != null) || (p instanceof MissingPart)) { + private void updatePartInUseData(PartInUse piu, Part p, boolean ignoreMothballedUnits, + int ignoreSparesUnderQuality) { + + if (ignoreMothballedUnits && (null != p.getUnit()) && p.getUnit().isMothballed()) { + // Ignore it + } else if ((p.getUnit() != null) || (p instanceof MissingPart)) { piu.setUseCount(piu.getUseCount() + getQuantity(p)); } else { if (p.isPresent()) { - piu.setStoreCount(piu.getStoreCount() + getQuantity(p)); - piu.addSpare(p); + if (p.getQuality() < ignoreSparesUnderQuality) { + // Ignore it + } else { + piu.setStoreCount(piu.getStoreCount() + getQuantity(p)); + piu.addSpare(p); + } } else { piu.setTransferCount(piu.getTransferCount() + getQuantity(p)); } @@ -2257,7 +2265,7 @@ private void updatePartInUseData(PartInUse piu, Part p) { } /** Update the piu with the current campaign data */ - public void updatePartInUse(PartInUse piu) { + public void updatePartInUse(PartInUse piu, boolean ignoreMothballedUnits, int ignoreSparesUnderQuality) { piu.setUseCount(0); piu.setStoreCount(0); piu.setTransferCount(0); @@ -2265,7 +2273,7 @@ public void updatePartInUse(PartInUse piu) { getWarehouse().forEachPart(p -> { PartInUse newPiu = getPartInUse(p); if (piu.equals(newPiu)) { - updatePartInUseData(piu, p); + updatePartInUseData(piu, p, ignoreMothballedUnits, ignoreSparesUnderQuality); } }); for (IAcquisitionWork maybePart : shoppingList.getPartList()) { @@ -2278,7 +2286,7 @@ public void updatePartInUse(PartInUse piu) { } } - public Set getPartsInUse() { + public Set getPartsInUse(boolean ignoreMothballedUnits, int ignoreSparesUnderQuality) { // java.util.Set doesn't supply a get(Object) method, so we have to use a // java.util.Map Map inUse = new HashMap<>(); @@ -2292,7 +2300,7 @@ public Set getPartsInUse() { } else { inUse.put(piu, piu); } - updatePartInUseData(piu, p); + updatePartInUseData(piu, p, ignoreMothballedUnits, ignoreSparesUnderQuality); }); for (IAcquisitionWork maybePart : shoppingList.getPartList()) { if (!(maybePart instanceof Part)) { @@ -2312,7 +2320,10 @@ public Set getPartsInUse() { : (Part) maybePart) * maybePart.getQuantity()); } - return inUse.keySet(); + return inUse.keySet().stream() + // Hacky but otherwise we end up with zero lines when filtering things out + .filter(p -> p.getUseCount() != 0 || p.getStoreCount() != 0 || p.getPlannedCount() != 0) + .collect(Collectors.toSet()); } @Deprecated diff --git a/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java b/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java index 0fe1c04c25..b66ad8d6e6 100644 --- a/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java @@ -18,28 +18,35 @@ */ package mekhq.gui.dialog; -import java.awt.BorderLayout; +import java.awt.Container; import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.util.Collections; import java.util.Optional; +import java.util.ResourceBundle; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.GroupLayout; +import javax.swing.GroupLayout.Alignment; + +import static javax.swing.GroupLayout.Alignment; import javax.swing.JDialog; -import javax.swing.JPanel; +import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTable; +import javax.swing.LayoutStyle; import javax.swing.RowSorter; import javax.swing.SortOrder; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; import javax.swing.table.TableRowSorter; +import mekhq.MekHQ; import mekhq.campaign.Campaign; import mekhq.campaign.Quartermaster; import mekhq.campaign.parts.Part; @@ -56,7 +63,8 @@ */ public class PartsReportDialog extends JDialog { - private JPanel overviewPartsPanel; + private JCheckBox ignoreMothballedCheck; + private JComboBox ignoreSparesUnderQualityCB; private JTable overviewPartsInUseTable; private PartsInUseTableModel overviewPartsModel; @@ -74,8 +82,14 @@ public PartsReportDialog(CampaignGUI gui, boolean modal) { } private void initComponents() { - overviewPartsPanel = new JPanel(new BorderLayout()); - + + Container container = this.getContentPane(); + + GroupLayout layout = new GroupLayout(container); + layout.setAutoCreateGaps(true); + layout.setAutoCreateContainerGaps(true); + container.setLayout(layout); + overviewPartsModel = new PartsInUseTableModel(); overviewPartsInUseTable = new JTable(overviewPartsModel); overviewPartsInUseTable.setRowSelectionAllowed(false); @@ -239,24 +253,69 @@ public void actionPerformed(ActionEvent e) { new PartsInUseTableModel.ButtonColumn(overviewPartsInUseTable, addInBulk, PartsInUseTableModel.COL_BUTTON_GMADD_BULK); - overviewPartsPanel.add(new JScrollPane(overviewPartsInUseTable), BorderLayout.CENTER); - JPanel panButtons = new JPanel(new GridBagLayout()); + JScrollPane tableScroll = new JScrollPane(overviewPartsInUseTable); + + ignoreMothballedCheck = new JCheckBox("Ignore Mothballed Units"); + ignoreMothballedCheck.addActionListener(evt -> refreshOverviewPartsInUse()); + + String[] qualities; + if(!campaign.getCampaignOptions().isReverseQualityNames()) { + qualities = new String[] {" ", "B", "C", "D", "E", "F"}; + } else { + qualities = new String[] {" ", "E", "D", "C", "B", "A"}; + } + ignoreSparesUnderQualityCB = new JComboBox(qualities); + ignoreSparesUnderQualityCB.setMaximumSize(ignoreSparesUnderQualityCB.getPreferredSize()); + ignoreSparesUnderQualityCB.addActionListener(evt -> refreshOverviewPartsInUse()); + JLabel ignorePartsUnderLabel = new JLabel("Ignore Parts Below Quality"); + JButton btnClose = new JButton("Close"); btnClose.addActionListener(evt -> setVisible(false)); - panButtons.add(btnClose, new GridBagConstraints()); - overviewPartsPanel.add(panButtons, BorderLayout.PAGE_END); + + layout.setHorizontalGroup(layout.createParallelGroup() + .addComponent(tableScroll) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createSequentialGroup() + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(ignorePartsUnderLabel) + .addComponent(ignoreSparesUnderQualityCB) + .addComponent(ignoreMothballedCheck) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(btnClose)))); - this.setLayout(new BorderLayout()); - this.add(overviewPartsPanel, BorderLayout.CENTER); - setPreferredSize(new Dimension(1000, 800)); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(tableScroll) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(ignoreMothballedCheck) + .addComponent(ignorePartsUnderLabel) + .addComponent(ignoreSparesUnderQualityCB) + .addComponent(btnClose))); + + setPreferredSize(new Dimension(1400, 1000)); } + + /** + * @param rating String containing A to F or space, from combo box + * @return minimum internal quality level to use + */ + + private int getMinimumQuality(String rating) { + if (rating.equals(" ")) { + // The blank spot always means "everything", so minimum = lowest + return Part.QUALITY_A; + } else { + return Part.getQualityFromName(rating, campaign.getCampaignOptions().isReverseQualityNames()); + } + } + private void refreshOverviewSpecificPart(int row, PartInUse piu, IAcquisitionWork newPart) { if (piu.equals(new PartInUse((Part) newPart))) { // Simple update - campaign.updatePartInUse(piu); + campaign.updatePartInUse(piu, ignoreMothballedCheck.isSelected(), + getMinimumQuality((String) ignoreSparesUnderQualityCB.getSelectedItem())); overviewPartsModel.fireTableRowsUpdated(row, row); } else { // Some other part changed; fire a full refresh to be sure @@ -265,7 +324,8 @@ private void refreshOverviewSpecificPart(int row, PartInUse piu, IAcquisitionWor } private void refreshOverviewPartsInUse() { - overviewPartsModel.setData(campaign.getPartsInUse()); + overviewPartsModel.setData(campaign.getPartsInUse(ignoreMothballedCheck.isSelected(), + getMinimumQuality((String) ignoreSparesUnderQualityCB.getSelectedItem()))); TableColumnModel tcm = overviewPartsInUseTable.getColumnModel(); PartsInUseTableModel.ButtonColumn column = (PartsInUseTableModel.ButtonColumn) tcm .getColumn(PartsInUseTableModel.COL_BUTTON_GMADD) @@ -275,4 +335,5 @@ private void refreshOverviewPartsInUse() { .getCellRenderer(); column.setEnabled(campaign.isGM()); } + } diff --git a/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java b/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java index a8e105d52b..3bd36fb722 100644 --- a/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java +++ b/MekHQ/src/mekhq/gui/model/PartsInUseTableModel.java @@ -201,7 +201,7 @@ public int getAlignment(int column) { public int getPreferredWidth(int column) { return switch (column) { case COL_PART -> 260; - case COL_IN_USE, COL_STORED, COL_TONNAGE, COL_IN_TRANSFER -> 20; + case COL_IN_USE, COL_STORED, COL_TONNAGE, COL_IN_TRANSFER -> 15; case COL_COST -> 40; case COL_BUTTON_BUY, COL_BUTTON_SELL -> 25; case COL_BUTTON_GMADD -> 65; From 491962d10d2bf6b0e2caa261fbdbd608945c7977 Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 14 Oct 2024 12:23:37 -0700 Subject: [PATCH 019/118] PartsReportDialog - use properties file --- .../mekhq/resources/PartsReportDialog.properties | 4 ++++ MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java | 9 +++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 MekHQ/resources/mekhq/resources/PartsReportDialog.properties diff --git a/MekHQ/resources/mekhq/resources/PartsReportDialog.properties b/MekHQ/resources/mekhq/resources/PartsReportDialog.properties new file mode 100644 index 0000000000..be356fa132 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/PartsReportDialog.properties @@ -0,0 +1,4 @@ +Form.title=Parts in Use +btnClose.text=Close +chkIgnoreMothballed.text=Ignore Parts on Mothballed Units +lblIgnoreSparesUnderQuality.text=Ignore Spare Parts Under Quality \ No newline at end of file diff --git a/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java b/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java index b66ad8d6e6..1e1a8f41a3 100644 --- a/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java @@ -71,6 +71,9 @@ public class PartsReportDialog extends JDialog { private Campaign campaign; private CampaignGUI gui; + private final transient ResourceBundle resourceMap = ResourceBundle.getBundle( + "mekhq.resources.PartsReportDialog", MekHQ.getMHQOptions().getLocale()); + public PartsReportDialog(CampaignGUI gui, boolean modal) { super(gui.getFrame(), modal); this.gui = gui; @@ -83,6 +86,8 @@ public PartsReportDialog(CampaignGUI gui, boolean modal) { private void initComponents() { + this.setTitle(resourceMap.getString("Form.title")); + Container container = this.getContentPane(); GroupLayout layout = new GroupLayout(container); @@ -256,7 +261,7 @@ public void actionPerformed(ActionEvent e) { JScrollPane tableScroll = new JScrollPane(overviewPartsInUseTable); - ignoreMothballedCheck = new JCheckBox("Ignore Mothballed Units"); + ignoreMothballedCheck = new JCheckBox(resourceMap.getString("chkIgnoreMothballed.text")); ignoreMothballedCheck.addActionListener(evt -> refreshOverviewPartsInUse()); String[] qualities; @@ -268,7 +273,7 @@ public void actionPerformed(ActionEvent e) { ignoreSparesUnderQualityCB = new JComboBox(qualities); ignoreSparesUnderQualityCB.setMaximumSize(ignoreSparesUnderQualityCB.getPreferredSize()); ignoreSparesUnderQualityCB.addActionListener(evt -> refreshOverviewPartsInUse()); - JLabel ignorePartsUnderLabel = new JLabel("Ignore Parts Below Quality"); + JLabel ignorePartsUnderLabel = new JLabel(resourceMap.getString("lblIgnoreSparesUnderQuality.text")); JButton btnClose = new JButton("Close"); btnClose.addActionListener(evt -> setVisible(false)); From 29fb85035fa963c09271b9fe474cc56c4c15d014 Mon Sep 17 00:00:00 2001 From: algebro Date: Tue, 15 Oct 2024 13:36:49 -0400 Subject: [PATCH 020/118] remove reroll totals in camops contract market --- .../mekhq/gui/view/ContractSummaryPanel.java | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java b/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java index 37a7b8a71c..05894e444a 100644 --- a/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java +++ b/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java @@ -25,6 +25,7 @@ import mekhq.campaign.Campaign; import mekhq.campaign.JumpPath; import mekhq.campaign.market.contractMarket.AbstractContractMarket; +import mekhq.campaign.market.enums.ContractMarketMethod; import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.Contract; import mekhq.campaign.personnel.Person; @@ -52,6 +53,7 @@ public class ContractSummaryPanel extends JPanel { private int cmdRerolls; private int logRerolls; private int tranRerolls; + private ContractMarketMethod method; private JPanel mainPanel; @@ -76,16 +78,20 @@ public ContractSummaryPanel(Contract contract, Campaign campaign, boolean allowR this.contract = contract; this.campaign = campaign; this.allowRerolls = allowRerolls; + this.method = campaign.getCampaignOptions().getContractMarketMethod(); if (allowRerolls) { - Person admin = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_COMMAND, SkillType.S_NEG, SkillType.S_ADMIN); - cmdRerolls = (admin == null || admin.getSkill(SkillType.S_NEG) == null) - ? 0 : admin.getSkill(SkillType.S_NEG).getLevel(); - admin = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_LOGISTICS, SkillType.S_NEG, SkillType.S_ADMIN); - logRerolls = (admin == null || admin.getSkill(SkillType.S_NEG) == null) - ? 0 : admin.getSkill(SkillType.S_NEG).getLevel(); - admin = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_TRANSPORT, SkillType.S_NEG, SkillType.S_ADMIN); - tranRerolls = (admin == null || admin.getSkill(SkillType.S_NEG) == null) - ? 0 : admin.getSkill(SkillType.S_NEG).getLevel(); + if (method == ContractMarketMethod.CAM_OPS) { + cmdRerolls = 1; + logRerolls = 1; + tranRerolls = 1; + } else { + Person admin = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_COMMAND, SkillType.S_NEG, SkillType.S_ADMIN); + cmdRerolls = (admin == null || admin.getSkill(SkillType.S_NEG) == null) ? 0 : admin.getSkill(SkillType.S_NEG).getLevel(); + admin = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_LOGISTICS, SkillType.S_NEG, SkillType.S_ADMIN); + logRerolls = (admin == null || admin.getSkill(SkillType.S_NEG) == null) ? 0 : admin.getSkill(SkillType.S_NEG).getLevel(); + admin = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_TRANSPORT, SkillType.S_NEG, SkillType.S_ADMIN); + tranRerolls = (admin == null || admin.getSkill(SkillType.S_NEG) == null) ? 0 : admin.getSkill(SkillType.S_NEG).getLevel(); + } } initComponents(); @@ -565,7 +571,13 @@ private void setSupportRerollButtonText(JButton rerollButton) { } private String generateRerollText(int rerolls) { - return resourceMap.getString("lblRenegotiate.text") + " (" + rerolls + ')'; + StringBuilder text = new StringBuilder(resourceMap.getString("lblRenegotiate.text")); + if (method != ContractMarketMethod.CAM_OPS) { + text.append(" (") + .append(rerolls) + .append(')'); + } + return text.toString(); } public void refreshAmounts() { From 910716ac051a526d77087c32d4a6c11a75f77767 Mon Sep 17 00:00:00 2001 From: Weaver Date: Tue, 15 Oct 2024 15:14:17 -0700 Subject: [PATCH 021/118] Convert the Part Quality concept into an enum --- MekHQ/src/mekhq/campaign/Campaign.java | 88 ++++----- MekHQ/src/mekhq/campaign/Quartermaster.java | 3 +- .../campaign/market/enums/UnitMarketType.java | 48 ++--- MekHQ/src/mekhq/campaign/mission/Loot.java | 19 +- .../src/mekhq/campaign/parts/MissingPart.java | 2 +- MekHQ/src/mekhq/campaign/parts/Part.java | 108 +++------- MekHQ/src/mekhq/campaign/parts/PartInUse.java | 2 +- MekHQ/src/mekhq/campaign/parts/Refit.java | 4 +- .../campaign/parts/enums/PartQuality.java | 186 ++++++++++++++++++ .../storytrigger/AddUnitStoryTrigger.java | 3 +- MekHQ/src/mekhq/campaign/unit/Unit.java | 39 ++-- .../AbstractCompanyGenerator.java | 7 +- MekHQ/src/mekhq/gui/CampaignGUI.java | 3 +- .../gui/adapter/PartsTableMouseAdapter.java | 37 ++-- .../adapter/ProcurementTableMouseAdapter.java | 5 +- .../gui/adapter/UnitTableMouseAdapter.java | 34 ++-- MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java | 5 +- .../gui/dialog/MekHQUnitSelectorDialog.java | 3 +- .../gui/dialog/PersonnelMarketDialog.java | 3 +- .../PartQualityReportDialog.java | 23 +-- .../mekhq/gui/panes/CampaignOptionsPane.java | 24 +-- 21 files changed, 366 insertions(+), 280 deletions(-) create mode 100644 MekHQ/src/mekhq/campaign/parts/enums/PartQuality.java diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index c14e3e0a16..ad5b504619 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -72,6 +72,7 @@ import mekhq.campaign.mission.enums.ScenarioStatus; import mekhq.campaign.mod.am.InjuryUtil; import mekhq.campaign.parts.*; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.parts.equipment.AmmoBin; import mekhq.campaign.parts.equipment.EquipmentPart; import mekhq.campaign.parts.equipment.MissingEquipmentPart; @@ -728,7 +729,7 @@ public void purchaseShipSearchResult() { : calculatePartTransitTime(Compute.d6(2) - 2); getFinances().debit(TransactionType.UNIT_PURCHASE, getLocalDate(), cost, "Purchased " + en.getShortName()); - int quality = 3; + PartQuality quality = PartQuality.D; if (campaignOptions.isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); @@ -1316,7 +1317,7 @@ public void addTestUnit(TestUnit tu) { } /** - * Add a new unit to the campaign and set its quality to 3 (D). + * Add a new unit to the campaign and set its quality to D. * * @param en An Entity object that the new unit will be * wrapped around @@ -1326,7 +1327,7 @@ public void addTestUnit(TestUnit tu) { * @return The newly added unit */ public Unit addNewUnit(Entity en, boolean allowNewPilots, int days) { - return addNewUnit(en, allowNewPilots, days, 3); + return addNewUnit(en, allowNewPilots, days, PartQuality.D); } /** @@ -1342,12 +1343,7 @@ public Unit addNewUnit(Entity en, boolean allowNewPilots, int days) { * @throws IllegalArgumentException If the quality is not within the valid range * (0-5) */ - public Unit addNewUnit(Entity en, boolean allowNewPilots, int days, int quality) { - if ((quality < 0) || (quality > 5)) { - throw new IllegalArgumentException( - "Invalid quality in mekhq/campaign/Campaign.java/addNewUnit: " + quality); - } - + public Unit addNewUnit(Entity en, boolean allowNewPilots, int days, PartQuality quality) { Unit unit = new Unit(en, this); unit.setMaintenanceMultiplier(getCampaignOptions().getDefaultMaintenanceTime()); getHangar().addUnit(unit); @@ -7782,7 +7778,7 @@ public void doMaintenance(Unit u) { } } // it is time for a maintenance check - int qualityOrig = u.getQuality(); + PartQuality qualityOrig = u.getQuality(); String techName = "Nobody"; String techNameLinked = techName; if (null != tech) { @@ -7830,21 +7826,21 @@ public void doMaintenance(Unit u) { logger.info(maintenanceReport.toString()); } - int quality = u.getQuality(); + PartQuality quality = u.getQuality(); String qualityString; boolean reverse = getCampaignOptions().isReverseQualityNames(); - if (quality > qualityOrig) { - qualityString = "Overall quality improves from " - + Part.getQualityName(qualityOrig, reverse) + " to " + Part.getQualityName(quality, reverse) - + ""; - } else if (quality < qualityOrig) { - qualityString = "Overall quality declines from " - + Part.getQualityName(qualityOrig, reverse) + " to " + Part.getQualityName(quality, reverse) - + ""; + if (quality.toNumeric() > qualityOrig.toNumeric()) { + qualityString = ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor(), + "Overall quality improves from " + qualityOrig.toName(reverse) + + " to " + quality.toName(reverse)); + } else if (quality.toNumeric() < qualityOrig.toNumeric()) { + qualityString = ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor(), + "Overall quality declines from " + qualityOrig.toName(reverse) + + " to " + quality.toName(reverse)); } else { - qualityString = "Overall quality remains " + Part.getQualityName(quality, reverse); + qualityString = "Overall quality remains " + quality.toName(reverse); } String damageString = ""; if (nDamage > 0) { @@ -7876,7 +7872,7 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT if (!p.needsMaintenance()) { return null; } - int oldQuality = p.getQuality(); + PartQuality oldQuality = p.getQuality(); TargetRoll target = getTargetForMaintenance(p, u.getTech()); if (!paidMaintenance) { // TODO : Make this modifier user inputtable @@ -7889,7 +7885,7 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT partReport += " rolled a " + roll + ", margin of " + margin; switch (p.getQuality()) { - case Part.QUALITY_A: { + case A: { if (margin >= 4) { p.improveQuality(); } @@ -7908,11 +7904,11 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT } break; } - case Part.QUALITY_B: { + case B: { if (margin >= 4) { p.improveQuality(); } else if (margin < -5) { - p.decreaseQuality(); + p.reduceQuality(); } if (!campaignOptions.isUseUnofficialMaintenance()) { if (margin < -6) { @@ -7923,9 +7919,9 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT } break; } - case Part.QUALITY_C: { + case C: { if (margin < -4) { - p.decreaseQuality(); + p.reduceQuality(); } else if (margin >= 5) { p.improveQuality(); } @@ -7938,9 +7934,9 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT } break; } - case Part.QUALITY_D: { + case D: { if (margin < -3) { - p.decreaseQuality(); + p.reduceQuality(); if ((margin < -4) && !campaignOptions.isUseUnofficialMaintenance()) { partsToDamage.put(p, 1); } @@ -7949,9 +7945,9 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT } break; } - case Part.QUALITY_E: + case E: if (margin < -2) { - p.decreaseQuality(); + p.reduceQuality(); if ((margin < -5) && !campaignOptions.isUseUnofficialMaintenance()) { partsToDamage.put(p, 1); } @@ -7959,10 +7955,10 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT p.improveQuality(); } break; - case Part.QUALITY_F: + case F: default: if (margin < -2) { - p.decreaseQuality(); + p.reduceQuality(); if (margin < -6 && !campaignOptions.isUseUnofficialMaintenance()) { partsToDamage.put(p, 1); } @@ -7973,22 +7969,26 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT // } break; } - if (p.getQuality() > oldQuality) { - partReport += ": new quality is " - + p.getQualityName() + ""; - } else if (p.getQuality() < oldQuality) { - partReport += ": new quality is " - + p.getQualityName() + ""; + if (p.getQuality().toNumeric() > oldQuality.toNumeric()) { + partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor(), + "new quality is " + p.getQualityName()); + } else if (p.getQuality().toNumeric() < oldQuality.toNumeric()) { + partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), + "new quality is " + p.getQualityName()); } else { partReport += ": quality remains " + p.getQualityName(); } if (null != partsToDamage.get(p)) { if (partsToDamage.get(p) > 3) { - partReport += ", part destroyed"; + partReport += ", " + ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), + "part destroyed"); } else { - partReport += ", part damaged"; + partReport += ", " + ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), + "part damaged"); } } diff --git a/MekHQ/src/mekhq/campaign/Quartermaster.java b/MekHQ/src/mekhq/campaign/Quartermaster.java index f0b6e9d378..de7eb75d61 100644 --- a/MekHQ/src/mekhq/campaign/Quartermaster.java +++ b/MekHQ/src/mekhq/campaign/Quartermaster.java @@ -28,6 +28,7 @@ import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.TransactionType; import mekhq.campaign.parts.*; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.parts.equipment.AmmoBin; import mekhq.campaign.unit.TestUnit; import mekhq.campaign.unit.Unit; @@ -466,7 +467,7 @@ public void arrivePart(Part part) { public boolean buyUnit(Entity en, int days) { Objects.requireNonNull(en); - int quality = 3; + PartQuality quality = PartQuality.D; if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); diff --git a/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java b/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java index 51cbbbc9e5..bd60956050 100644 --- a/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java +++ b/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java @@ -18,14 +18,13 @@ */ package mekhq.campaign.market.enums; -import java.util.HashMap; import java.util.ResourceBundle; import megamek.common.Compute; import megamek.logging.MMLogger; import mekhq.MekHQ; import mekhq.campaign.Campaign; -import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.unit.Unit; public enum UnitMarketType { @@ -162,41 +161,22 @@ public static int getPricePercentage(int modifier) { * @param market the type of market * @return the quality of the unit */ - public static int getQuality(Campaign campaign, UnitMarketType market) { - HashMap qualityAndModifier = new HashMap<>(); - - switch (market) { - case OPEN: - case MERCENARY: - qualityAndModifier.put("quality", Part.QUALITY_C); - qualityAndModifier.put("modifier", 0); - break; - case EMPLOYER: - qualityAndModifier.put("quality", Part.QUALITY_B); - qualityAndModifier.put("modifier", -1); - break; - case BLACK_MARKET: - if (Compute.d6(1) <= 2) { - qualityAndModifier.put("quality", Part.QUALITY_A); - // this is to force a result of 0 (A) - qualityAndModifier.put("modifier", -12); - } else { - qualityAndModifier.put("quality", Part.QUALITY_F); - // this is to force a result of 5 (F) - qualityAndModifier.put("modifier", 12); - } - break; - case FACTORY: - qualityAndModifier.put("quality", Part.QUALITY_F); - // this is to force a result of 5 (F) - qualityAndModifier.put("modifier", 12); - break; - } + public static PartQuality getQuality(Campaign campaign, UnitMarketType market) { if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { - return Unit.getRandomUnitQuality(qualityAndModifier.get("modifier")); + return Unit.getRandomUnitQuality(switch(market) { + case OPEN, MERCENARY -> 0; + case EMPLOYER -> -1; + case BLACK_MARKET -> Compute.d6(1) <= 2 ? -12 : 12; // forces A/F + case FACTORY -> 12; // Forces F + }); } else { - return qualityAndModifier.get("quality"); + return switch(market) { + case OPEN, MERCENARY -> PartQuality.C; + case EMPLOYER -> PartQuality.B; + case BLACK_MARKET -> Compute.d6(1) <= 2 ? PartQuality.A : PartQuality.F; + case FACTORY -> PartQuality.F; + }; } } } diff --git a/MekHQ/src/mekhq/campaign/mission/Loot.java b/MekHQ/src/mekhq/campaign/mission/Loot.java index bd2c2c3bd3..214fbf352d 100644 --- a/MekHQ/src/mekhq/campaign/mission/Loot.java +++ b/MekHQ/src/mekhq/campaign/mission/Loot.java @@ -38,6 +38,7 @@ import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.TransactionType; import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.rating.IUnitRating; import mekhq.campaign.unit.Unit; import mekhq.utilities.MHQXMLUtility; @@ -177,10 +178,12 @@ public void getLoot(Campaign campaign, Scenario scenario) { logger.debug("Looting unit: {}", e.getDisplayName()); if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { - qualityAndModifier.put("quality", Unit.getRandomUnitQuality(qualityAndModifier.get("modifier"))); + qualityAndModifier.put("quality", + Unit.getRandomUnitQuality(qualityAndModifier.get("modifier")).toNumeric()); } - campaign.addNewUnit(e, false, 0, qualityAndModifier.get("quality")); + campaign.addNewUnit(e, false, 0, + PartQuality.fromNumeric(qualityAndModifier.get("quality"))); logger.debug("Looting units complete"); } @@ -203,24 +206,24 @@ private static HashMap getQualityAndModifier(Mission contract) if (contract instanceof AtBContract) { switch (((AtBContract) contract).getEnemyQuality()) { case IUnitRating.DRAGOON_F: - qualityAndModifier.put("quality", Part.QUALITY_A); + qualityAndModifier.put("quality", PartQuality.A.toNumeric()); qualityAndModifier.put("modifier", -2); break; case IUnitRating.DRAGOON_D: - qualityAndModifier.put("quality", Part.QUALITY_B); + qualityAndModifier.put("quality", PartQuality.B.toNumeric()); qualityAndModifier.put("modifier", -1); break; case IUnitRating.DRAGOON_C: case IUnitRating.DRAGOON_B: - qualityAndModifier.put("quality", Part.QUALITY_C); + qualityAndModifier.put("quality", PartQuality.C.toNumeric()); qualityAndModifier.put("modifier", 0); break; case IUnitRating.DRAGOON_A: - qualityAndModifier.put("quality", Part.QUALITY_D); + qualityAndModifier.put("quality", PartQuality.D.toNumeric()); qualityAndModifier.put("modifier", 1); break; case IUnitRating.DRAGOON_ASTAR: - qualityAndModifier.put("quality", Part.QUALITY_F); + qualityAndModifier.put("quality", PartQuality.F.toNumeric()); qualityAndModifier.put("modifier", 2); break; default: @@ -229,7 +232,7 @@ private static HashMap getQualityAndModifier(Mission contract) + ((AtBContract) contract).getEnemyQuality()); } } else { - qualityAndModifier.put("quality", 3); + qualityAndModifier.put("quality", PartQuality.D.toNumeric()); qualityAndModifier.put("modifier", 0); } diff --git a/MekHQ/src/mekhq/campaign/parts/MissingPart.java b/MekHQ/src/mekhq/campaign/parts/MissingPart.java index 6678137aaf..241cc6fa6b 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingPart.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingPart.java @@ -177,7 +177,7 @@ public Part findReplacement(boolean refit) { return part; } else if (bestPart.needsFixing() && !part.needsFixing()) { return part; - } else if (bestPart.getQuality() < part.getQuality()) { + } else if (bestPart.getQuality().toNumeric() < part.getQuality().toNumeric()) { return part; } } diff --git a/MekHQ/src/mekhq/campaign/parts/Part.java b/MekHQ/src/mekhq/campaign/parts/Part.java index e84081be09..2d3ed0c2e0 100644 --- a/MekHQ/src/mekhq/campaign/parts/Part.java +++ b/MekHQ/src/mekhq/campaign/parts/Part.java @@ -30,6 +30,7 @@ import mekhq.campaign.Campaign; import mekhq.campaign.finances.Money; import mekhq.campaign.parts.enums.PartRepairType; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.parts.equipment.EquipmentPart; import mekhq.campaign.parts.equipment.MissingEquipmentPart; import mekhq.campaign.personnel.Person; @@ -81,13 +82,6 @@ public abstract class Part implements IPartWork, ITechnology { public static final int T_IS = 1; public static final int T_CLAN = 2; - public static final int QUALITY_A = 0; - public static final int QUALITY_B = 1; - public static final int QUALITY_C = 2; - public static final int QUALITY_D = 3; - public static final int QUALITY_E = 4; - public static final int QUALITY_F = 5; - protected static final TechAdvancement TA_POD = Entity.getOmniAdvancement(); // Generic TechAdvancement for a number of basic components. protected static final TechAdvancement TA_GENERIC = new TechAdvancement(TECH_BASE_ALL) @@ -121,7 +115,7 @@ public abstract class Part implements IPartWork, ITechnology { // null is valid. It indicates parts that are not attached to units. protected Unit unit; - protected int quality; + protected PartQuality quality; protected boolean brandNew; @@ -197,50 +191,13 @@ public Part(int tonnage, boolean omniPodded, Campaign c) { this.campaign = c; this.brandNew = true; this.quantity = 1; - this.quality = QUALITY_D; + this.quality = PartQuality.D; this.childParts = new ArrayList<>(); this.isTeamSalvaging = false; } - public static String getQualityName(int quality, boolean reverse) { - switch (quality) { - case QUALITY_A: - if (reverse) { - return "F"; - } - return "A"; - case QUALITY_B: - if (reverse) { - return "E"; - } - return "B"; - case QUALITY_C: - if (reverse) { - return "D"; - } - return "C"; - case QUALITY_D: - if (reverse) { - return "C"; - } - return "D"; - case QUALITY_E: - if (reverse) { - return "B"; - } - return "E"; - case QUALITY_F: - if (reverse) { - return "A"; - } - return "F"; - default: - return "?"; - } - } - public String getQualityName() { - return getQualityName(getQuality(), campaign.getCampaignOptions().isReverseQualityNames()); + return quality.toName(campaign.getCampaignOptions().isReverseQualityNames()); } public void setId(int id) { @@ -308,7 +265,8 @@ public Money adjustCostsForCampaignOptions(@Nullable Money cost) { } if (!isBrandNew()) { - cost = cost.multipliedBy(campaign.getCampaignOptions().getUsedPartPriceMultipliers()[getQuality()]); + cost = cost.multipliedBy(campaign.getCampaignOptions() + .getUsedPartPriceMultipliers()[getQuality().toNumeric()]); } if (needsFixing() && !isPriceAdjustedForAmount()) { @@ -563,16 +521,18 @@ public boolean isSamePartTypeAndStatus(Part part) { public abstract boolean isSamePartType(Part part); - public boolean isSameStatus(Part part) { + public boolean isSameStatus(Part otherPart) { // parts that are reserved for refit or being worked on are never the same // status if (isReservedForRefit() || isBeingWorkedOn() || isReservedForReplacement() || hasParentPart() - || part.isReservedForRefit() || part.isBeingWorkedOn() || part.isReservedForReplacement() - || part.hasParentPart()) { + || otherPart.isReservedForRefit() || otherPart.isBeingWorkedOn() + || otherPart.isReservedForReplacement() || otherPart.hasParentPart()) { return false; } - return quality == part.getQuality() && hits == part.getHits() && part.getSkillMin() == this.getSkillMin() - && this.getDaysToArrival() == part.getDaysToArrival(); + return getQuality() == otherPart.getQuality() + && getHits() == otherPart.getHits() + && getSkillMin() == otherPart.getSkillMin() + && getDaysToArrival() == otherPart.getDaysToArrival(); } protected boolean isClanTechBase() { @@ -640,7 +600,7 @@ protected int writeToXMLBegin(final PrintWriter pw, int indent) { if (reservedBy != null) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "reserveId", reservedBy.getId()); } - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "quality", quality); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "quality", quality.toNumeric()); if (isTeamSalvaging) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "isTeamSalvaging", true); } @@ -747,7 +707,7 @@ public static Part generateInstanceFromXML(Node wn, Version version) { } else if (wn2.getNodeName().equalsIgnoreCase("replacementId")) { retVal.replacementPart = new PartRef(Integer.parseInt(wn2.getTextContent())); } else if (wn2.getNodeName().equalsIgnoreCase("quality")) { - retVal.quality = Integer.parseInt(wn2.getTextContent()); + retVal.quality = PartQuality.fromNumeric(Integer.parseInt(wn2.getTextContent())); } else if (wn2.getNodeName().equalsIgnoreCase("parentPartId")) { retVal.parentPart = new PartRef(Integer.parseInt(wn2.getTextContent())); } else if (wn2.getNodeName().equalsIgnoreCase("childPartId")) { @@ -978,31 +938,9 @@ public TargetRoll getAllModsForMaintenance() { * @return the modified {@link TargetRoll} */ private TargetRoll getQualityMods(TargetRoll mods, Person tech) { - int qualityMod = 0; - switch (quality) { - case QUALITY_A: - qualityMod = 3; - break; - case QUALITY_B: - qualityMod = 2; - break; - case QUALITY_C: - qualityMod = 1; - break; - case QUALITY_D: - qualityMod = 0; - break; - case QUALITY_E: - qualityMod = -1; - break; - case QUALITY_F: - qualityMod = -2; - break; - } - mods.addModifier(qualityMod, getQualityName(quality, campaign.getCampaignOptions().isReverseQualityNames())); - if ((qualityMod > 0) && - (null != tech) && - tech.getOptions().booleanOption(PersonnelOptions.TECH_FIXER)) { + mods.addModifier(getQuality().getRepairModifier(), getQualityName()); + if ((getQuality().getRepairModifier() > 0) + && (null != tech) && tech.getOptions().booleanOption(PersonnelOptions.TECH_FIXER)) { // fixers can ignore the first point of penalty for poor quality mods.addModifier(-1, "Mr/Ms Fix-it"); } @@ -1409,19 +1347,19 @@ public void doMaintenanceDamage(int d) { updateConditionFromEntity(false); } - public int getQuality() { + public PartQuality getQuality() { return quality; } public void improveQuality() { - quality += 1; + quality = quality.improveQuality(); } - public void decreaseQuality() { - quality -= 1; + public void reduceQuality() { + quality = quality.reduceQuality(); } - public void setQuality(int q) { + public void setQuality(PartQuality q) { quality = q; } diff --git a/MekHQ/src/mekhq/campaign/parts/PartInUse.java b/MekHQ/src/mekhq/campaign/parts/PartInUse.java index 5c751fe380..50012f64e5 100644 --- a/MekHQ/src/mekhq/campaign/parts/PartInUse.java +++ b/MekHQ/src/mekhq/campaign/parts/PartInUse.java @@ -92,7 +92,7 @@ public String getDescription() { */ public List getSpares() { return spares.stream() - .sorted(Comparator.comparingInt(Part::getQuality)) + .sorted(Comparator.comparing(Part::getQuality)) .collect(Collectors.toList()); } diff --git a/MekHQ/src/mekhq/campaign/parts/Refit.java b/MekHQ/src/mekhq/campaign/parts/Refit.java index df7cbb9fce..08927cfc41 100644 --- a/MekHQ/src/mekhq/campaign/parts/Refit.java +++ b/MekHQ/src/mekhq/campaign/parts/Refit.java @@ -1580,9 +1580,7 @@ private void complete() { if (isRefurbishing) { for (Part p : oldUnit.getParts()) { - if (p.getQuality() != QUALITY_F) { - p.improveQuality(); - } + p.improveQuality(); } } MekHQ.triggerEvent(new UnitRefitEvent(oldUnit)); diff --git a/MekHQ/src/mekhq/campaign/parts/enums/PartQuality.java b/MekHQ/src/mekhq/campaign/parts/enums/PartQuality.java new file mode 100644 index 0000000000..1fb0034c0d --- /dev/null +++ b/MekHQ/src/mekhq/campaign/parts/enums/PartQuality.java @@ -0,0 +1,186 @@ +/* + * PartQuality.java + * + * Copyright (c) 2009 - Jay Lawson (jaylawson39 at yahoo.com). All Rights Reserved. + * Copyright (c) 2022 - 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.parts.enums; + +import mekhq.MekHQ; + +import java.util.*; + +/** + * Represents the quality of a Part. Quality is a scale that ranges from A to F. By the book, A + * is bad and F is good, but there is an option that inverts this scale, hence the 'reverse' + * options on the various functions available here. + * + * Internally quality is represented by a number 0 to 5, bad to good. + */ +public enum PartQuality { + A(0), + B(1), + C(2), + D(3), + E(4), + F(5); + + public final int numericQuality; + + private PartQuality(int quality) { + this.numericQuality = quality; + } + + /** + * @return numeric quality 0-5 bad-good + */ + public int toNumeric() { + return this.numericQuality; + } + + /** + * @param rawQuality - numeric quality 0-5 bad-good + * @return corresponding PartQuality + * @throws IllegalArgumentException + */ + public static PartQuality fromNumeric(int rawQuality) { + return switch (rawQuality) { + case 0 -> A; + case 1 -> B; + case 2 -> C; + case 3 -> D; + case 4 -> E; + case 5 -> F; + default -> throw new IllegalArgumentException("rawQuality must be int 0-5"); + }; + } + + /** + * @param reverse - are quality names reversed per the campaign option + * @return String letter name for quality A-F bad-good (or good-bad if reversed) + */ + public String toName(boolean reversed) { + if (!reversed) { + return switch(this) { + case A -> "A"; + case B -> "B"; + case C -> "C"; + case D -> "D"; + case E -> "E"; + case F -> "F"; + }; + } else { + return switch(this) { + case B -> "E"; + case A -> "F"; + case C -> "D"; + case D -> "C"; + case E -> "B"; + case F -> "A"; + default -> "?"; + }; + } + } + + /** + * @param code - one-character String name from A-F bad-good (or good-bad if reversed) + * @param reverse - are quality names reversed per the campaign option + * @return corresponding PartQuality + * @throws IllegalArgumentException + */ + public static PartQuality fromName(String code, boolean reverse) { + if (!reverse) { + return switch(code) { + case "A" -> PartQuality.A; + case "B" -> PartQuality.B; + case "C" -> PartQuality.C; + case "D" -> PartQuality.D; + case "E" -> PartQuality.E; + case "F" -> PartQuality.F; + default -> throw new IllegalArgumentException("Expecting one-char string A to F"); + }; + } else { + return switch(code) { + case "F" -> PartQuality.A; + case "E" -> PartQuality.B; + case "D" -> PartQuality.C; + case "C" -> PartQuality.D; + case "B" -> PartQuality.E; + case "A" -> PartQuality.F; + default -> throw new IllegalArgumentException("Expecting one-char string A to F"); + }; + } + } + + /** + * @return modifier for repair rolls using a part of this quality + */ + public int getRepairModifier(){ + return switch(this) { + case A -> 3; + case B -> 2; + case C -> 1; + case D -> 0; + case E -> -1; + case F -> -2; + }; + } + + /** + * @return Hex color code for coloring parts of this quality. + */ + public String getHexColor() { + return switch (this) { + case A, B -> MekHQ.getMHQOptions().getFontColorNegativeHexColor(); + case C, D -> MekHQ.getMHQOptions().getFontColorWarningHexColor(); + case E, F -> MekHQ.getMHQOptions().getFontColorPositiveHexColor(); + + }; + } + + /** + * @return PartQuality that is one step better than this one, clamped + */ + public PartQuality improveQuality() { + if (this == F) { + return this; + } else { + return fromNumeric(toNumeric() + 1); + } + } + + /** + * @return PartQuality that is one step worse than this one, clamped + */ + public PartQuality reduceQuality() { + if (this == A) { + return this; + } else { + return fromNumeric(toNumeric() - 1); + } + } + + /** + * @return A list of PartQualities in order bad to good + */ + public static List allQualities() { + return List.of(A,B,C,D,E,F); + } +} \ No newline at end of file diff --git a/MekHQ/src/mekhq/campaign/storyarc/storytrigger/AddUnitStoryTrigger.java b/MekHQ/src/mekhq/campaign/storyarc/storytrigger/AddUnitStoryTrigger.java index 5710d595ed..ae5091f706 100644 --- a/MekHQ/src/mekhq/campaign/storyarc/storytrigger/AddUnitStoryTrigger.java +++ b/MekHQ/src/mekhq/campaign/storyarc/storytrigger/AddUnitStoryTrigger.java @@ -33,6 +33,7 @@ import megamek.common.MekSummaryCache; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.storyarc.StoryTrigger; import mekhq.campaign.unit.Unit; import mekhq.utilities.MHQXMLUtility; @@ -63,7 +64,7 @@ protected void execute() { Entity en = mekFileParser.getEntity(); - int quality = 3; + PartQuality quality = PartQuality.D; if (getCampaign().getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); diff --git a/MekHQ/src/mekhq/campaign/unit/Unit.java b/MekHQ/src/mekhq/campaign/unit/Unit.java index e256d17599..4b3eca43f6 100644 --- a/MekHQ/src/mekhq/campaign/unit/Unit.java +++ b/MekHQ/src/mekhq/campaign/unit/Unit.java @@ -52,6 +52,7 @@ import mekhq.campaign.mission.Mission; import mekhq.campaign.mission.Scenario; import mekhq.campaign.parts.*; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.parts.equipment.*; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.PersonnelOptions; @@ -76,8 +77,6 @@ import java.util.function.Predicate; import java.util.stream.Collectors; -import static mekhq.campaign.parts.Part.*; - /** * This is a wrapper class for entity, so that we can add some functionality to * it @@ -1163,12 +1162,12 @@ public Money getSellValue() { double[] usedPartPriceMultipliers = campaign.getCampaignOptions().getUsedPartPriceMultipliers(); return switch (this.getQuality()) { - case QUALITY_A -> unitCost.multipliedBy(usedPartPriceMultipliers[0]); - case QUALITY_B -> unitCost.multipliedBy(usedPartPriceMultipliers[1]); - case QUALITY_C -> unitCost.multipliedBy(usedPartPriceMultipliers[2]); - case QUALITY_D -> unitCost.multipliedBy(usedPartPriceMultipliers[3]); - case QUALITY_E -> unitCost.multipliedBy(usedPartPriceMultipliers[4]); - case QUALITY_F -> unitCost.multipliedBy(usedPartPriceMultipliers[5]); + case A -> unitCost.multipliedBy(usedPartPriceMultipliers[0]); + case B -> unitCost.multipliedBy(usedPartPriceMultipliers[1]); + case C -> unitCost.multipliedBy(usedPartPriceMultipliers[2]); + case D -> unitCost.multipliedBy(usedPartPriceMultipliers[3]); + case E -> unitCost.multipliedBy(usedPartPriceMultipliers[4]); + case F -> unitCost.multipliedBy(usedPartPriceMultipliers[5]); default -> throw new IllegalStateException("Unexpected value in mekhq/campaign/unit/Unit.java/getSellValue: " + this.getQuality()); @@ -5227,7 +5226,7 @@ public void setMaintenanceMultiplier(int value) { maintenanceMultiplier = value; } - public int getQuality() { + public PartQuality getQuality() { int nParts = 0; int sumQuality = 0; for (Part p : getParts()) { @@ -5236,16 +5235,16 @@ public int getQuality() { nParts++; } else if (p.needsMaintenance()) { nParts++; - sumQuality += p.getQuality(); + sumQuality += p.getQuality().toNumeric(); } } if (nParts == 0) { - return QUALITY_D; + return PartQuality.D; } - return (int) Math.round((1.0 * sumQuality) / nParts); + return PartQuality.fromNumeric((int) Math.round((1.0 * sumQuality) / nParts)); } - public void setQuality(int q) { + public void setQuality(PartQuality q) { for (Part p : getParts()) { if (!(p instanceof MissingPart)) { p.setQuality(q); @@ -5254,7 +5253,7 @@ public void setQuality(int q) { } public String getQualityName() { - return Part.getQualityName(getQuality(), getCampaign().getCampaignOptions().isReverseQualityNames()); + return getQuality().toName(getCampaign().getCampaignOptions().isReverseQualityNames()); } public boolean requiresMaintenance() { @@ -5887,18 +5886,18 @@ public void fixReferences(Campaign campaign) { * @throws IllegalStateException if an unexpected value is encountered during * the switch statement */ - public static int getRandomUnitQuality(int modifier) { + public static PartQuality getRandomUnitQuality(int modifier) { int roll = MathUtility.clamp( (Compute.d6(2) + modifier), 2, 12); return switch (roll) { - case 2, 3, 4, 5 -> QUALITY_A; - case 6, 7, 8 -> QUALITY_B; - case 9, 10 -> QUALITY_C; - case 11 -> QUALITY_D; - case 12 -> QUALITY_F; + case 2, 3, 4, 5 -> PartQuality.A; + case 6, 7, 8 -> PartQuality.B; + case 9, 10 -> PartQuality.C; + case 11 -> PartQuality.D; + case 12 -> PartQuality.F; default -> throw new IllegalStateException( "Unexpected value in mekhq/campaign/unit/Unit.java/getRandomUnitQuality: " + roll); }; diff --git a/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java b/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java index 2ea402711d..38257232e5 100644 --- a/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java +++ b/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java @@ -40,6 +40,7 @@ import mekhq.campaign.parts.AmmoStorage; import mekhq.campaign.parts.Armor; import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.parts.equipment.AmmoBin; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.Skill; @@ -994,7 +995,7 @@ private List createUnits(final Campaign campaign, continue; } - int quality = 3; + PartQuality quality = PartQuality.D; if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { int modifier = 0; @@ -1367,12 +1368,12 @@ public List generateMothballedEntities(final Campaign campaign, */ private List createMothballedSpareUnits(final Campaign campaign, final List mothballedEntities) { - int quality; + PartQuality quality; if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); } else { - quality = 3; + quality = PartQuality.D; } final List mothballedUnits = mothballedEntities.stream() diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index e570d55a8a..474c62f4d5 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -51,6 +51,7 @@ import mekhq.campaign.mission.Scenario; import mekhq.campaign.parts.Part; import mekhq.campaign.parts.Refit; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.autoAwards.AutoAwardsController; @@ -1914,7 +1915,7 @@ private File checkFileEnding(File file, String format) { protected void loadListFile(final boolean allowNewPilots) { final File unitFile = FileDialogs.openUnits(getFrame()).orElse(null); - int quality = 3; + PartQuality quality = PartQuality.D; if (getCampaign().getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); diff --git a/MekHQ/src/mekhq/gui/adapter/PartsTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/PartsTableMouseAdapter.java index ad9d0b2af0..06296d4a27 100644 --- a/MekHQ/src/mekhq/gui/adapter/PartsTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/PartsTableMouseAdapter.java @@ -37,6 +37,7 @@ import mekhq.campaign.parts.AmmoStorage; import mekhq.campaign.parts.Armor; import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.work.WorkTime; import mekhq.gui.CampaignGUI; import mekhq.gui.dialog.MRMSDialog; @@ -129,31 +130,25 @@ public void actionPerformed(ActionEvent action) { } } } else if (command.contains("SET_QUALITY")) { - int q = -1; - boolean reverse = gui.getCampaign().getCampaignOptions().isReverseQualityNames(); + boolean reverse = gui.getCampaign().getCampaignOptions().isReverseQualityNames(); + // TODO : Duplicated in UnitTableMouseAdapter#actionPerformed Object[] possibilities = { - Part.getQualityName(Part.QUALITY_A, reverse), - Part.getQualityName(Part.QUALITY_B, reverse), - Part.getQualityName(Part.QUALITY_C, reverse), - Part.getQualityName(Part.QUALITY_D, reverse), - Part.getQualityName(Part.QUALITY_E, reverse), - Part.getQualityName(Part.QUALITY_F, reverse) + PartQuality.A.toName(reverse), + PartQuality.B.toName(reverse), + PartQuality.C.toName(reverse), + PartQuality.D.toName(reverse), + PartQuality.E.toName(reverse), + PartQuality.F.toName(reverse) }; String quality = (String) JOptionPane.showInputDialog(gui.getFrame(), "Choose the new quality level", "Set Quality", JOptionPane.PLAIN_MESSAGE, null, possibilities, - Part.getQualityName(Part.QUALITY_D, reverse)); - for (int i = 0; i < possibilities.length; i++) { - if (possibilities[i].equals(quality)) { - q = i; - break; - } - } - if (q != -1) { - for (Part p : parts) { - if (p != null) { - p.setQuality(q); - MekHQ.triggerEvent(new PartChangedEvent(p)); - } + PartQuality.D.toName(reverse)); + + PartQuality q = PartQuality.fromName(quality, reverse); + for (Part p : parts) { + if (null != p) { + p.setQuality(q); + MekHQ.triggerEvent(new PartChangedEvent(p)); } } } else if (command.contains("CHANGE_MODE")) { diff --git a/MekHQ/src/mekhq/gui/adapter/ProcurementTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/ProcurementTableMouseAdapter.java index 9bd2086e59..0f4d133f31 100644 --- a/MekHQ/src/mekhq/gui/adapter/ProcurementTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/ProcurementTableMouseAdapter.java @@ -33,6 +33,7 @@ import mekhq.MekHQ; import mekhq.campaign.event.ProcurementEvent; import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.unit.Unit; import mekhq.campaign.work.IAcquisitionWork; import mekhq.gui.CampaignGUI; @@ -207,12 +208,12 @@ private void addOneItem(final IAcquisitionWork acquisition) { if (equipment instanceof Part) { gui.getCampaign().getQuartermaster().addPart((Part) equipment, 0); } else if (equipment instanceof Entity) { - int quality; + PartQuality quality; if (gui.getCampaign().getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); } else { - quality = 3; + quality = PartQuality.D; } gui.getCampaign().addNewUnit((Entity) equipment, false, 0, quality); diff --git a/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java index 02d1714e54..ff819d6c9c 100644 --- a/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java @@ -40,6 +40,7 @@ import mekhq.campaign.parts.Part; import mekhq.campaign.parts.Refit; import mekhq.campaign.parts.equipment.AmmoBin; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.personnel.Person; import mekhq.campaign.unit.Unit; import mekhq.campaign.unit.actions.*; @@ -193,31 +194,24 @@ public void actionPerformed(ActionEvent action) { new PartQualityReportDialog(gui.getFrame(), selectedUnit).setVisible(true); } else if (command.equals(COMMAND_SET_QUALITY)) { // TODO : Duplicated in PartsTableMouseAdapter#actionPerformed - int q = -1; boolean reverse = gui.getCampaign().getCampaignOptions().isReverseQualityNames(); Object[] possibilities = { - Part.getQualityName(Part.QUALITY_A, reverse), - Part.getQualityName(Part.QUALITY_B, reverse), - Part.getQualityName(Part.QUALITY_C, reverse), - Part.getQualityName(Part.QUALITY_D, reverse), - Part.getQualityName(Part.QUALITY_E, reverse), - Part.getQualityName(Part.QUALITY_F, reverse) + PartQuality.A.toName(reverse), + PartQuality.B.toName(reverse), + PartQuality.C.toName(reverse), + PartQuality.D.toName(reverse), + PartQuality.E.toName(reverse), + PartQuality.F.toName(reverse) }; String quality = (String) JOptionPane.showInputDialog(gui.getFrame(), "Choose the new quality level", "Set Quality", JOptionPane.PLAIN_MESSAGE, null, possibilities, - Part.getQualityName(Part.QUALITY_D, reverse)); - for (int i = 0; i < possibilities.length; i++) { - if (possibilities[i].equals(quality)) { - q = i; - break; - } - } - if (q != -1) { - for (Unit unit : units) { - if (unit != null) { - unit.setQuality(q); - MekHQ.triggerEvent(new UnitChangedEvent(unit)); - } + PartQuality.D.toName(reverse)); + + PartQuality q = PartQuality.fromName(quality, reverse); + for (Unit unit : units) { + if (null != unit) { + unit.setQuality(q); + MekHQ.triggerEvent(new UnitChangedEvent(unit)); } } } else if (command.equals(COMMAND_SELL)) { diff --git a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java index 5a3730a9aa..8de15b3259 100644 --- a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java @@ -34,6 +34,7 @@ import mekhq.MekHQ; import mekhq.campaign.event.PersonChangedEvent; import mekhq.campaign.mission.AtBDynamicScenarioFactory; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.personnel.Bloodname; import mekhq.campaign.personnel.Clan; import mekhq.campaign.personnel.Person; @@ -1511,12 +1512,12 @@ private void addRATRolledUnit() { } if (getLastRolledUnit() != null) { - int quality; + PartQuality quality; if (getGUI().getCampaign().getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); } else { - quality = 3; + quality = PartQuality.D; } final Unit unit = getGUI().getCampaign().addNewUnit(getLastRolledUnit(), false, 0, quality); diff --git a/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java b/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java index b96c8f745a..5b717674ed 100644 --- a/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java @@ -23,6 +23,7 @@ import megamek.client.ui.swing.dialog.AbstractUnitSelectorDialog; import megamek.common.*; import mekhq.campaign.Campaign; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.unit.UnitOrder; import mekhq.campaign.unit.UnitTechProgression; @@ -124,7 +125,7 @@ protected JPanel createButtonsPanel() { protected void select(boolean isGM) { if (getSelectedEntity() != null) { if (isGM) { - int quality = 3; + PartQuality quality = PartQuality.D; if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { quality = UnitOrder.getRandomUnitQuality(0); diff --git a/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java b/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java index 2fef873d16..c6c6150c28 100644 --- a/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java @@ -60,6 +60,7 @@ import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.TransactionType; import mekhq.campaign.market.PersonnelMarket; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.unit.Unit; @@ -415,7 +416,7 @@ private void addUnit(Entity en, boolean pay) { return; } - int quality = 3; + PartQuality quality = PartQuality.D; if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { quality = UnitOrder.getRandomUnitQuality(0); diff --git a/MekHQ/src/mekhq/gui/dialog/reportDialogs/PartQualityReportDialog.java b/MekHQ/src/mekhq/gui/dialog/reportDialogs/PartQualityReportDialog.java index d97d699b08..f10130ddeb 100644 --- a/MekHQ/src/mekhq/gui/dialog/reportDialogs/PartQualityReportDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/reportDialogs/PartQualityReportDialog.java @@ -20,16 +20,13 @@ import mekhq.MekHQ; import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.unit.Unit; import mekhq.utilities.ReportingUtilities; import javax.swing.*; import java.util.*; -import static mekhq.campaign.parts.Part.QUALITY_A; -import static mekhq.campaign.parts.Part.QUALITY_B; -import static mekhq.campaign.parts.Part.QUALITY_E; -import static mekhq.campaign.parts.Part.QUALITY_F; import static mekhq.utilities.ReportingUtilities.CLOSING_SPAN_TAG; /** @@ -113,7 +110,7 @@ private String getPartsReport(Unit unit) { for (String location : locations) { report.append(""); if (location.equals(unit.getName())) { - String colorCode = getColorCode(unit.getQuality()); + String colorCode = unit.getQuality().getHexColor(); // Add the location and its colored quality rating to the report. report.append("") @@ -134,8 +131,7 @@ private String getPartsReport(Unit unit) { for (Part part : reportMap.get(location)) { report.append(part.getName()).append(" - "); - int qualityLevel = part.getQuality(); - String colorCode = getColorCode(qualityLevel); + String colorCode = part.getQuality().getHexColor(); report.append(ReportingUtilities.spanOpeningWithCustomColor(colorCode)) .append(part.getQualityName()).append(CLOSING_SPAN_TAG).append("
"); @@ -151,17 +147,4 @@ private String getPartsReport(Unit unit) { return report.toString(); } - /** - * Returns the color code associated with the given quality level. - * - * @param qualityLevel The quality level for which to retrieve the color code. - * @return The color code associated with the quality level. - */ - private static String getColorCode(int qualityLevel) { - return switch (qualityLevel) { - case QUALITY_A, QUALITY_B -> MekHQ.getMHQOptions().getFontColorNegativeHexColor(); - case QUALITY_E, QUALITY_F -> MekHQ.getMHQOptions().getFontColorPositiveHexColor(); - default -> MekHQ.getMHQOptions().getFontColorWarningHexColor(); - }; - } } diff --git a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java index 8ffa181617..5600b366d9 100644 --- a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java +++ b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java @@ -54,6 +54,7 @@ import mekhq.campaign.mission.AtBContract; import mekhq.campaign.mission.enums.AtBLanceRole; import mekhq.campaign.parts.Part; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.personnel.PersonnelOptions; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.Skills; @@ -7556,25 +7557,26 @@ private JPanel createUsedPartsValueMultipliersPanel(boolean reverseQualities) { .createTitledBorder(resources.getString("usedPartsValueMultipliersPanel.title"))); panel.setName("usedPartsValueMultipliersPanel"); - spnUsedPartPriceMultipliers = new JSpinner[Part.QUALITY_F + 1]; + spnUsedPartPriceMultipliers = new JSpinner[PartQuality.F.toNumeric() + 1]; - for (int i = Part.QUALITY_A; i <= Part.QUALITY_F; i++) { - final String qualityLevel = Part.getQualityName(i, reverseQualities); + for (PartQuality q : PartQuality.allQualities()) { + final String qualityLevel = q.toName(reverseQualities); final JLabel label = new JLabel(qualityLevel); label.setToolTipText(resources.getString("lblUsedPartPriceMultiplier.toolTipText")); label.setName("lbl" + qualityLevel); panel.add(label); - spnUsedPartPriceMultipliers[i] = new JSpinner(new SpinnerNumberModel(0.00, 0.00, 1.00, 0.05)); - spnUsedPartPriceMultipliers[i] + spnUsedPartPriceMultipliers[q.toNumeric()] = new JSpinner( + new SpinnerNumberModel(0.00, 0.00, 1.00, 0.05)); + spnUsedPartPriceMultipliers[q.toNumeric()] .setToolTipText(resources.getString("lblUsedPartPriceMultiplier.toolTipText")); - spnUsedPartPriceMultipliers[i].setName("spn" + qualityLevel); - spnUsedPartPriceMultipliers[i] - .setEditor(new NumberEditor(spnUsedPartPriceMultipliers[i], "0.00")); - panel.add(spnUsedPartPriceMultipliers[i]); + spnUsedPartPriceMultipliers[q.toNumeric()].setName("spn" + qualityLevel); + spnUsedPartPriceMultipliers[q.toNumeric()] + .setEditor(new NumberEditor(spnUsedPartPriceMultipliers[q.toNumeric()], "0.00")); + panel.add(spnUsedPartPriceMultipliers[q.toNumeric()]); - label.setLabelFor(spnUsedPartPriceMultipliers[i]); + label.setLabelFor(spnUsedPartPriceMultipliers[q.toNumeric()]); } return panel; @@ -9910,7 +9912,7 @@ private void recreateFinancesPanel(boolean reverseQualities) { spnCancelledOrderRefundMultiplier.setValue(options.getCancelledOrderRefundMultiplier()); // Used Parts Multiplier Panel - for (int index = Part.QUALITY_A; index <= Part.QUALITY_F; index++) { + for (int index = PartQuality.A.toNumeric(); index <= PartQuality.F.toNumeric(); index++) { spnUsedPartPriceMultipliers[index].setValue(options.getUsedPartPriceMultipliers()[index]); } From 55e4385d465e14b37383cf901a186dd678b90c9c Mon Sep 17 00:00:00 2001 From: Weaver Date: Tue, 15 Oct 2024 17:05:11 -0700 Subject: [PATCH 022/118] Part Quality Enum - changes and unit test fixes --- MekHQ/src/mekhq/campaign/Campaign.java | 16 +-- MekHQ/src/mekhq/campaign/Quartermaster.java | 2 +- .../campaign/market/enums/UnitMarketType.java | 8 +- MekHQ/src/mekhq/campaign/mission/Loot.java | 12 +- MekHQ/src/mekhq/campaign/parts/Part.java | 2 +- .../campaign/parts/enums/PartQuality.java | 107 +++++++++--------- .../storytrigger/AddUnitStoryTrigger.java | 2 +- MekHQ/src/mekhq/campaign/unit/Unit.java | 24 ++-- .../AbstractCompanyGenerator.java | 4 +- MekHQ/src/mekhq/gui/CampaignGUI.java | 2 +- .../gui/adapter/PartsTableMouseAdapter.java | 17 ++- .../adapter/ProcurementTableMouseAdapter.java | 2 +- .../gui/adapter/UnitTableMouseAdapter.java | 15 ++- MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java | 2 +- .../gui/dialog/MekHQUnitSelectorDialog.java | 2 +- .../gui/dialog/PersonnelMarketDialog.java | 2 +- .../mekhq/gui/panes/CampaignOptionsPane.java | 22 ++-- .../mekhq/campaign/QuartermasterTest.java | 13 ++- .../campaign/unit/UnitTestUtilities.java | 3 +- 19 files changed, 128 insertions(+), 129 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 8209a29a18..636b3c6015 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -729,7 +729,7 @@ public void purchaseShipSearchResult() { : calculatePartTransitTime(Compute.d6(2) - 2); getFinances().debit(TransactionType.UNIT_PURCHASE, getLocalDate(), cost, "Purchased " + en.getShortName()); - PartQuality quality = PartQuality.D; + PartQuality quality = PartQuality.QUALITY_D; if (campaignOptions.isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); @@ -1327,7 +1327,7 @@ public void addTestUnit(TestUnit tu) { * @return The newly added unit */ public Unit addNewUnit(Entity en, boolean allowNewPilots, int days) { - return addNewUnit(en, allowNewPilots, days, PartQuality.D); + return addNewUnit(en, allowNewPilots, days, PartQuality.QUALITY_D); } /** @@ -7884,7 +7884,7 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT partReport += " rolled a " + roll + ", margin of " + margin; switch (p.getQuality()) { - case A: { + case QUALITY_A: { if (margin >= 4) { p.improveQuality(); } @@ -7903,7 +7903,7 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT } break; } - case B: { + case QUALITY_B: { if (margin >= 4) { p.improveQuality(); } else if (margin < -5) { @@ -7918,7 +7918,7 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT } break; } - case C: { + case QUALITY_C: { if (margin < -4) { p.reduceQuality(); } else if (margin >= 5) { @@ -7933,7 +7933,7 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT } break; } - case D: { + case QUALITY_D: { if (margin < -3) { p.reduceQuality(); if ((margin < -4) && !campaignOptions.isUseUnofficialMaintenance()) { @@ -7944,7 +7944,7 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT } break; } - case E: + case QUALITY_E: if (margin < -2) { p.reduceQuality(); if ((margin < -5) && !campaignOptions.isUseUnofficialMaintenance()) { @@ -7954,7 +7954,7 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT p.improveQuality(); } break; - case F: + case QUALITY_F: default: if (margin < -2) { p.reduceQuality(); diff --git a/MekHQ/src/mekhq/campaign/Quartermaster.java b/MekHQ/src/mekhq/campaign/Quartermaster.java index de7eb75d61..69329b4cb6 100644 --- a/MekHQ/src/mekhq/campaign/Quartermaster.java +++ b/MekHQ/src/mekhq/campaign/Quartermaster.java @@ -467,7 +467,7 @@ public void arrivePart(Part part) { public boolean buyUnit(Entity en, int days) { Objects.requireNonNull(en); - PartQuality quality = PartQuality.D; + PartQuality quality = PartQuality.QUALITY_D; if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); diff --git a/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java b/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java index bd60956050..a8c242caa2 100644 --- a/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java +++ b/MekHQ/src/mekhq/campaign/market/enums/UnitMarketType.java @@ -172,10 +172,10 @@ public static PartQuality getQuality(Campaign campaign, UnitMarketType market) { }); } else { return switch(market) { - case OPEN, MERCENARY -> PartQuality.C; - case EMPLOYER -> PartQuality.B; - case BLACK_MARKET -> Compute.d6(1) <= 2 ? PartQuality.A : PartQuality.F; - case FACTORY -> PartQuality.F; + case OPEN, MERCENARY -> PartQuality.QUALITY_C; + case EMPLOYER -> PartQuality.QUALITY_B; + case BLACK_MARKET -> Compute.d6(1) <= 2 ? PartQuality.QUALITY_A : PartQuality.QUALITY_F; + case FACTORY -> PartQuality.QUALITY_F; }; } } diff --git a/MekHQ/src/mekhq/campaign/mission/Loot.java b/MekHQ/src/mekhq/campaign/mission/Loot.java index 2b8ddbf1b5..eb35a5f8d0 100644 --- a/MekHQ/src/mekhq/campaign/mission/Loot.java +++ b/MekHQ/src/mekhq/campaign/mission/Loot.java @@ -206,24 +206,24 @@ private static HashMap getQualityAndModifier(Mission contract) if (contract instanceof AtBContract) { switch (((AtBContract) contract).getEnemyQuality()) { case IUnitRating.DRAGOON_F: - qualityAndModifier.put("quality", PartQuality.A.toNumeric()); + qualityAndModifier.put("quality", PartQuality.QUALITY_A.toNumeric()); qualityAndModifier.put("modifier", -2); break; case IUnitRating.DRAGOON_D: - qualityAndModifier.put("quality", PartQuality.B.toNumeric()); + qualityAndModifier.put("quality", PartQuality.QUALITY_B.toNumeric()); qualityAndModifier.put("modifier", -1); break; case IUnitRating.DRAGOON_C: case IUnitRating.DRAGOON_B: - qualityAndModifier.put("quality", PartQuality.C.toNumeric()); + qualityAndModifier.put("quality", PartQuality.QUALITY_C.toNumeric()); qualityAndModifier.put("modifier", 0); break; case IUnitRating.DRAGOON_A: - qualityAndModifier.put("quality", PartQuality.D.toNumeric()); + qualityAndModifier.put("quality", PartQuality.QUALITY_D.toNumeric()); qualityAndModifier.put("modifier", 1); break; case IUnitRating.DRAGOON_ASTAR: - qualityAndModifier.put("quality", PartQuality.F.toNumeric()); + qualityAndModifier.put("quality", PartQuality.QUALITY_F.toNumeric()); qualityAndModifier.put("modifier", 2); break; default: @@ -232,7 +232,7 @@ private static HashMap getQualityAndModifier(Mission contract) + ((AtBContract) contract).getEnemyQuality()); } } else { - qualityAndModifier.put("quality", PartQuality.D.toNumeric()); + qualityAndModifier.put("quality", PartQuality.QUALITY_D.toNumeric()); qualityAndModifier.put("modifier", 0); } diff --git a/MekHQ/src/mekhq/campaign/parts/Part.java b/MekHQ/src/mekhq/campaign/parts/Part.java index 2d3ed0c2e0..6ec3232139 100644 --- a/MekHQ/src/mekhq/campaign/parts/Part.java +++ b/MekHQ/src/mekhq/campaign/parts/Part.java @@ -191,7 +191,7 @@ public Part(int tonnage, boolean omniPodded, Campaign c) { this.campaign = c; this.brandNew = true; this.quantity = 1; - this.quality = PartQuality.D; + this.quality = PartQuality.QUALITY_D; this.childParts = new ArrayList<>(); this.isTeamSalvaging = false; } diff --git a/MekHQ/src/mekhq/campaign/parts/enums/PartQuality.java b/MekHQ/src/mekhq/campaign/parts/enums/PartQuality.java index 1fb0034c0d..dfa0e7410a 100644 --- a/MekHQ/src/mekhq/campaign/parts/enums/PartQuality.java +++ b/MekHQ/src/mekhq/campaign/parts/enums/PartQuality.java @@ -1,8 +1,7 @@ /* * PartQuality.java * - * Copyright (c) 2009 - Jay Lawson (jaylawson39 at yahoo.com). All Rights Reserved. - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -35,12 +34,12 @@ * Internally quality is represented by a number 0 to 5, bad to good. */ public enum PartQuality { - A(0), - B(1), - C(2), - D(3), - E(4), - F(5); + QUALITY_A(0), + QUALITY_B(1), + QUALITY_C(2), + QUALITY_D(3), + QUALITY_E(4), + QUALITY_F(5); public final int numericQuality; @@ -62,38 +61,38 @@ public int toNumeric() { */ public static PartQuality fromNumeric(int rawQuality) { return switch (rawQuality) { - case 0 -> A; - case 1 -> B; - case 2 -> C; - case 3 -> D; - case 4 -> E; - case 5 -> F; + case 0 -> QUALITY_A; + case 1 -> QUALITY_B; + case 2 -> QUALITY_C; + case 3 -> QUALITY_D; + case 4 -> QUALITY_E; + case 5 -> QUALITY_F; default -> throw new IllegalArgumentException("rawQuality must be int 0-5"); }; } /** - * @param reverse - are quality names reversed per the campaign option + * @param reversed - are quality names reversed per the campaign option * @return String letter name for quality A-F bad-good (or good-bad if reversed) */ public String toName(boolean reversed) { if (!reversed) { return switch(this) { - case A -> "A"; - case B -> "B"; - case C -> "C"; - case D -> "D"; - case E -> "E"; - case F -> "F"; + case QUALITY_A -> "A"; + case QUALITY_B -> "B"; + case QUALITY_C -> "C"; + case QUALITY_D -> "D"; + case QUALITY_E -> "E"; + case QUALITY_F -> "F"; }; } else { return switch(this) { - case B -> "E"; - case A -> "F"; - case C -> "D"; - case D -> "C"; - case E -> "B"; - case F -> "A"; + case QUALITY_B -> "E"; + case QUALITY_A -> "F"; + case QUALITY_C -> "D"; + case QUALITY_D -> "C"; + case QUALITY_E -> "B"; + case QUALITY_F -> "A"; default -> "?"; }; } @@ -101,29 +100,29 @@ public String toName(boolean reversed) { /** * @param code - one-character String name from A-F bad-good (or good-bad if reversed) - * @param reverse - are quality names reversed per the campaign option + * @param reversed - are quality names reversed per the campaign option * @return corresponding PartQuality * @throws IllegalArgumentException */ - public static PartQuality fromName(String code, boolean reverse) { - if (!reverse) { + public static PartQuality fromName(String code, boolean reversed) { + if (!reversed) { return switch(code) { - case "A" -> PartQuality.A; - case "B" -> PartQuality.B; - case "C" -> PartQuality.C; - case "D" -> PartQuality.D; - case "E" -> PartQuality.E; - case "F" -> PartQuality.F; + case "A" -> PartQuality.QUALITY_A; + case "B" -> PartQuality.QUALITY_B; + case "C" -> PartQuality.QUALITY_C; + case "D" -> PartQuality.QUALITY_D; + case "E" -> PartQuality.QUALITY_E; + case "F" -> PartQuality.QUALITY_F; default -> throw new IllegalArgumentException("Expecting one-char string A to F"); }; } else { return switch(code) { - case "F" -> PartQuality.A; - case "E" -> PartQuality.B; - case "D" -> PartQuality.C; - case "C" -> PartQuality.D; - case "B" -> PartQuality.E; - case "A" -> PartQuality.F; + case "F" -> PartQuality.QUALITY_A; + case "E" -> PartQuality.QUALITY_B; + case "D" -> PartQuality.QUALITY_C; + case "C" -> PartQuality.QUALITY_D; + case "B" -> PartQuality.QUALITY_E; + case "A" -> PartQuality.QUALITY_F; default -> throw new IllegalArgumentException("Expecting one-char string A to F"); }; } @@ -134,12 +133,12 @@ public static PartQuality fromName(String code, boolean reverse) { */ public int getRepairModifier(){ return switch(this) { - case A -> 3; - case B -> 2; - case C -> 1; - case D -> 0; - case E -> -1; - case F -> -2; + case QUALITY_A -> 3; + case QUALITY_B -> 2; + case QUALITY_C -> 1; + case QUALITY_D -> 0; + case QUALITY_E -> -1; + case QUALITY_F -> -2; }; } @@ -148,9 +147,9 @@ public int getRepairModifier(){ */ public String getHexColor() { return switch (this) { - case A, B -> MekHQ.getMHQOptions().getFontColorNegativeHexColor(); - case C, D -> MekHQ.getMHQOptions().getFontColorWarningHexColor(); - case E, F -> MekHQ.getMHQOptions().getFontColorPositiveHexColor(); + case QUALITY_A, QUALITY_B -> MekHQ.getMHQOptions().getFontColorNegativeHexColor(); + case QUALITY_C, QUALITY_D -> MekHQ.getMHQOptions().getFontColorWarningHexColor(); + case QUALITY_E, QUALITY_F -> MekHQ.getMHQOptions().getFontColorPositiveHexColor(); }; } @@ -159,7 +158,7 @@ public String getHexColor() { * @return PartQuality that is one step better than this one, clamped */ public PartQuality improveQuality() { - if (this == F) { + if (this == QUALITY_F) { return this; } else { return fromNumeric(toNumeric() + 1); @@ -170,7 +169,7 @@ public PartQuality improveQuality() { * @return PartQuality that is one step worse than this one, clamped */ public PartQuality reduceQuality() { - if (this == A) { + if (this == QUALITY_A) { return this; } else { return fromNumeric(toNumeric() - 1); @@ -181,6 +180,6 @@ public PartQuality reduceQuality() { * @return A list of PartQualities in order bad to good */ public static List allQualities() { - return List.of(A,B,C,D,E,F); + return List.of(QUALITY_A,QUALITY_B,QUALITY_C,QUALITY_D,QUALITY_E,QUALITY_F); } } \ No newline at end of file diff --git a/MekHQ/src/mekhq/campaign/storyarc/storytrigger/AddUnitStoryTrigger.java b/MekHQ/src/mekhq/campaign/storyarc/storytrigger/AddUnitStoryTrigger.java index ae5091f706..72fb12a110 100644 --- a/MekHQ/src/mekhq/campaign/storyarc/storytrigger/AddUnitStoryTrigger.java +++ b/MekHQ/src/mekhq/campaign/storyarc/storytrigger/AddUnitStoryTrigger.java @@ -64,7 +64,7 @@ protected void execute() { Entity en = mekFileParser.getEntity(); - PartQuality quality = PartQuality.D; + PartQuality quality = PartQuality.QUALITY_D; if (getCampaign().getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); diff --git a/MekHQ/src/mekhq/campaign/unit/Unit.java b/MekHQ/src/mekhq/campaign/unit/Unit.java index 4b3eca43f6..86813a1ea2 100644 --- a/MekHQ/src/mekhq/campaign/unit/Unit.java +++ b/MekHQ/src/mekhq/campaign/unit/Unit.java @@ -1162,12 +1162,12 @@ public Money getSellValue() { double[] usedPartPriceMultipliers = campaign.getCampaignOptions().getUsedPartPriceMultipliers(); return switch (this.getQuality()) { - case A -> unitCost.multipliedBy(usedPartPriceMultipliers[0]); - case B -> unitCost.multipliedBy(usedPartPriceMultipliers[1]); - case C -> unitCost.multipliedBy(usedPartPriceMultipliers[2]); - case D -> unitCost.multipliedBy(usedPartPriceMultipliers[3]); - case E -> unitCost.multipliedBy(usedPartPriceMultipliers[4]); - case F -> unitCost.multipliedBy(usedPartPriceMultipliers[5]); + case QUALITY_A -> unitCost.multipliedBy(usedPartPriceMultipliers[0]); + case QUALITY_B -> unitCost.multipliedBy(usedPartPriceMultipliers[1]); + case QUALITY_C -> unitCost.multipliedBy(usedPartPriceMultipliers[2]); + case QUALITY_D -> unitCost.multipliedBy(usedPartPriceMultipliers[3]); + case QUALITY_E -> unitCost.multipliedBy(usedPartPriceMultipliers[4]); + case QUALITY_F -> unitCost.multipliedBy(usedPartPriceMultipliers[5]); default -> throw new IllegalStateException("Unexpected value in mekhq/campaign/unit/Unit.java/getSellValue: " + this.getQuality()); @@ -5239,7 +5239,7 @@ public PartQuality getQuality() { } } if (nParts == 0) { - return PartQuality.D; + return PartQuality.QUALITY_D; } return PartQuality.fromNumeric((int) Math.round((1.0 * sumQuality) / nParts)); } @@ -5893,11 +5893,11 @@ public static PartQuality getRandomUnitQuality(int modifier) { 12); return switch (roll) { - case 2, 3, 4, 5 -> PartQuality.A; - case 6, 7, 8 -> PartQuality.B; - case 9, 10 -> PartQuality.C; - case 11 -> PartQuality.D; - case 12 -> PartQuality.F; + case 2, 3, 4, 5 -> PartQuality.QUALITY_A; + case 6, 7, 8 -> PartQuality.QUALITY_B; + case 9, 10 -> PartQuality.QUALITY_C; + case 11 -> PartQuality.QUALITY_D; + case 12 -> PartQuality.QUALITY_F; default -> throw new IllegalStateException( "Unexpected value in mekhq/campaign/unit/Unit.java/getRandomUnitQuality: " + roll); }; diff --git a/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java b/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java index 38257232e5..421c2fe8cd 100644 --- a/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java +++ b/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java @@ -995,7 +995,7 @@ private List createUnits(final Campaign campaign, continue; } - PartQuality quality = PartQuality.D; + PartQuality quality = PartQuality.QUALITY_D; if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { int modifier = 0; @@ -1373,7 +1373,7 @@ private List createMothballedSpareUnits(final Campaign campaign, if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); } else { - quality = PartQuality.D; + quality = PartQuality.QUALITY_D; } final List mothballedUnits = mothballedEntities.stream() diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index 474c62f4d5..e9fd1a20f0 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -1915,7 +1915,7 @@ private File checkFileEnding(File file, String format) { protected void loadListFile(final boolean allowNewPilots) { final File unitFile = FileDialogs.openUnits(getFrame()).orElse(null); - PartQuality quality = PartQuality.D; + PartQuality quality = PartQuality.QUALITY_D; if (getCampaign().getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); diff --git a/MekHQ/src/mekhq/gui/adapter/PartsTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/PartsTableMouseAdapter.java index 06296d4a27..17bf017e0d 100644 --- a/MekHQ/src/mekhq/gui/adapter/PartsTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/PartsTableMouseAdapter.java @@ -130,19 +130,18 @@ public void actionPerformed(ActionEvent action) { } } } else if (command.contains("SET_QUALITY")) { - boolean reverse = gui.getCampaign().getCampaignOptions().isReverseQualityNames(); - // TODO : Duplicated in UnitTableMouseAdapter#actionPerformed + boolean reverse = gui.getCampaign().getCampaignOptions().isReverseQualityNames(); Object[] possibilities = { - PartQuality.A.toName(reverse), - PartQuality.B.toName(reverse), - PartQuality.C.toName(reverse), - PartQuality.D.toName(reverse), - PartQuality.E.toName(reverse), - PartQuality.F.toName(reverse) + PartQuality.QUALITY_A.toName(reverse), + PartQuality.QUALITY_B.toName(reverse), + PartQuality.QUALITY_C.toName(reverse), + PartQuality.QUALITY_D.toName(reverse), + PartQuality.QUALITY_E.toName(reverse), + PartQuality.QUALITY_F.toName(reverse) }; String quality = (String) JOptionPane.showInputDialog(gui.getFrame(), "Choose the new quality level", "Set Quality", JOptionPane.PLAIN_MESSAGE, null, possibilities, - PartQuality.D.toName(reverse)); + PartQuality.QUALITY_D.toName(reverse)); PartQuality q = PartQuality.fromName(quality, reverse); for (Part p : parts) { diff --git a/MekHQ/src/mekhq/gui/adapter/ProcurementTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/ProcurementTableMouseAdapter.java index 0f4d133f31..4246b845e6 100644 --- a/MekHQ/src/mekhq/gui/adapter/ProcurementTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/ProcurementTableMouseAdapter.java @@ -213,7 +213,7 @@ private void addOneItem(final IAcquisitionWork acquisition) { if (gui.getCampaign().getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); } else { - quality = PartQuality.D; + quality = PartQuality.QUALITY_D; } gui.getCampaign().addNewUnit((Entity) equipment, false, 0, quality); diff --git a/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java index d45e1d4432..6da4a925ca 100644 --- a/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java @@ -193,19 +193,18 @@ public void actionPerformed(ActionEvent action) { } else if (command.equals(COMMAND_PARTS_REPORT)) { // Single Unit only new PartQualityReportDialog(gui.getFrame(), selectedUnit).setVisible(true); } else if (command.equals(COMMAND_SET_QUALITY)) { - // TODO : Duplicated in PartsTableMouseAdapter#actionPerformed boolean reverse = gui.getCampaign().getCampaignOptions().isReverseQualityNames(); Object[] possibilities = { - PartQuality.A.toName(reverse), - PartQuality.B.toName(reverse), - PartQuality.C.toName(reverse), - PartQuality.D.toName(reverse), - PartQuality.E.toName(reverse), - PartQuality.F.toName(reverse) + PartQuality.QUALITY_A.toName(reverse), + PartQuality.QUALITY_B.toName(reverse), + PartQuality.QUALITY_C.toName(reverse), + PartQuality.QUALITY_D.toName(reverse), + PartQuality.QUALITY_E.toName(reverse), + PartQuality.QUALITY_F.toName(reverse) }; String quality = (String) JOptionPane.showInputDialog(gui.getFrame(), "Choose the new quality level", "Set Quality", JOptionPane.PLAIN_MESSAGE, null, possibilities, - PartQuality.D.toName(reverse)); + PartQuality.QUALITY_D.toName(reverse)); PartQuality q = PartQuality.fromName(quality, reverse); for (Unit unit : units) { diff --git a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java index 8de15b3259..a0a88bff35 100644 --- a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java @@ -1517,7 +1517,7 @@ private void addRATRolledUnit() { if (getGUI().getCampaign().getCampaignOptions().isUseRandomUnitQualities()) { quality = Unit.getRandomUnitQuality(0); } else { - quality = PartQuality.D; + quality = PartQuality.QUALITY_D; } final Unit unit = getGUI().getCampaign().addNewUnit(getLastRolledUnit(), false, 0, quality); diff --git a/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java b/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java index 5b717674ed..d8c39d43b1 100644 --- a/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/MekHQUnitSelectorDialog.java @@ -125,7 +125,7 @@ protected JPanel createButtonsPanel() { protected void select(boolean isGM) { if (getSelectedEntity() != null) { if (isGM) { - PartQuality quality = PartQuality.D; + PartQuality quality = PartQuality.QUALITY_D; if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { quality = UnitOrder.getRandomUnitQuality(0); diff --git a/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java b/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java index c6c6150c28..e9be5763fd 100644 --- a/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java @@ -416,7 +416,7 @@ private void addUnit(Entity en, boolean pay) { return; } - PartQuality quality = PartQuality.D; + PartQuality quality = PartQuality.QUALITY_D; if (campaign.getCampaignOptions().isUseRandomUnitQualities()) { quality = UnitOrder.getRandomUnitQuality(0); diff --git a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java index 5600b366d9..c4c4a75acd 100644 --- a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java +++ b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java @@ -7557,26 +7557,26 @@ private JPanel createUsedPartsValueMultipliersPanel(boolean reverseQualities) { .createTitledBorder(resources.getString("usedPartsValueMultipliersPanel.title"))); panel.setName("usedPartsValueMultipliersPanel"); - spnUsedPartPriceMultipliers = new JSpinner[PartQuality.F.toNumeric() + 1]; + spnUsedPartPriceMultipliers = new JSpinner[PartQuality.QUALITY_F.toNumeric() + 1]; - for (PartQuality q : PartQuality.allQualities()) { - final String qualityLevel = q.toName(reverseQualities); + for (PartQuality quality : PartQuality.allQualities()) { + final String qualityLevel = quality.toName(reverseQualities); final JLabel label = new JLabel(qualityLevel); label.setToolTipText(resources.getString("lblUsedPartPriceMultiplier.toolTipText")); label.setName("lbl" + qualityLevel); panel.add(label); - spnUsedPartPriceMultipliers[q.toNumeric()] = new JSpinner( + spnUsedPartPriceMultipliers[quality.toNumeric()] = new JSpinner( new SpinnerNumberModel(0.00, 0.00, 1.00, 0.05)); - spnUsedPartPriceMultipliers[q.toNumeric()] + spnUsedPartPriceMultipliers[quality.toNumeric()] .setToolTipText(resources.getString("lblUsedPartPriceMultiplier.toolTipText")); - spnUsedPartPriceMultipliers[q.toNumeric()].setName("spn" + qualityLevel); - spnUsedPartPriceMultipliers[q.toNumeric()] - .setEditor(new NumberEditor(spnUsedPartPriceMultipliers[q.toNumeric()], "0.00")); - panel.add(spnUsedPartPriceMultipliers[q.toNumeric()]); + spnUsedPartPriceMultipliers[quality.toNumeric()].setName("spn" + qualityLevel); + spnUsedPartPriceMultipliers[quality.toNumeric()] + .setEditor(new NumberEditor(spnUsedPartPriceMultipliers[quality.toNumeric()], "0.00")); + panel.add(spnUsedPartPriceMultipliers[quality.toNumeric()]); - label.setLabelFor(spnUsedPartPriceMultipliers[q.toNumeric()]); + label.setLabelFor(spnUsedPartPriceMultipliers[quality.toNumeric()]); } return panel; @@ -9912,7 +9912,7 @@ private void recreateFinancesPanel(boolean reverseQualities) { spnCancelledOrderRefundMultiplier.setValue(options.getCancelledOrderRefundMultiplier()); // Used Parts Multiplier Panel - for (int index = PartQuality.A.toNumeric(); index <= PartQuality.F.toNumeric(); index++) { + for (int index = PartQuality.QUALITY_A.toNumeric(); index <= PartQuality.QUALITY_F.toNumeric(); index++) { spnUsedPartPriceMultipliers[index].setValue(options.getUsedPartPriceMultipliers()[index]); } diff --git a/MekHQ/unittests/mekhq/campaign/QuartermasterTest.java b/MekHQ/unittests/mekhq/campaign/QuartermasterTest.java index 769fbe7754..7917438ff8 100644 --- a/MekHQ/unittests/mekhq/campaign/QuartermasterTest.java +++ b/MekHQ/unittests/mekhq/campaign/QuartermasterTest.java @@ -30,6 +30,7 @@ import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.TransactionType; import mekhq.campaign.parts.*; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.unit.TestUnit; import mekhq.campaign.unit.Unit; import org.junit.jupiter.api.Test; @@ -248,7 +249,7 @@ public void buyUnitAddsUnconditionallyIfNotPayingForUnits() { assertTrue(quartermaster.buyUnit(mockEntity, transitDays)); // ...and the new unit should be added to the campaign. - verify(mockCampaign, times(1)).addNewUnit(eq(mockEntity), eq(false), eq(transitDays), eq(3)); + verify(mockCampaign, times(1)).addNewUnit(eq(mockEntity), eq(false), eq(transitDays), eq(PartQuality.QUALITY_D)); } @Test @@ -273,7 +274,7 @@ public void buyUnitReturnsFalseIfOutOfCash() { assertFalse(quartermaster.buyUnit(mockEntity, 0)); // ...and the new unit should NOT be added to the campaign. - verify(mockCampaign, times(0)).addNewUnit(eq(mockEntity), eq(false), eq(0), eq(3)); + verify(mockCampaign, times(0)).addNewUnit(eq(mockEntity), eq(false), eq(0), eq(PartQuality.QUALITY_D)); } @Test @@ -301,7 +302,7 @@ public void buyUnitBuysAUnitIfWeCanAffordIt() { assertTrue(quartermaster.buyUnit(mockEntity, 0)); // ...and the new unit should be added to the campaign... - verify(mockCampaign, times(1)).addNewUnit(eq(mockEntity), eq(false), eq(0), eq(3)); + verify(mockCampaign, times(1)).addNewUnit(eq(mockEntity), eq(false), eq(0), eq(PartQuality.QUALITY_D)); // ...and it should cost the right amount. assertEquals(Money.of(cost), captor.getValue()); @@ -332,7 +333,7 @@ public void buyUnitBuysInfantryUsingAlternateCost() { assertTrue(quartermaster.buyUnit(mockEntity, 0)); // ...and the new infantry should be added to the campaign... - verify(mockCampaign, times(1)).addNewUnit(eq(mockEntity), eq(false), eq(0), eq(3)); + verify(mockCampaign, times(1)).addNewUnit(eq(mockEntity), eq(false), eq(0), eq(PartQuality.QUALITY_D)); // ...and it should cost the right amount. assertEquals(Money.of(cost), captor.getValue()); @@ -368,7 +369,7 @@ public void buyUnitAppliesClanCostMultiplier() { assertTrue(quartermaster.buyUnit(mockEntity, 0)); // ...and the new unit should be added to the campaign... - verify(mockCampaign, times(1)).addNewUnit(eq(mockEntity), eq(false), eq(0), eq(3)); + verify(mockCampaign, times(1)).addNewUnit(eq(mockEntity), eq(false), eq(0), eq(PartQuality.QUALITY_D)); // ...and it should cost the right amount. assertEquals(Money.of(clanMultiplier * cost), captor.getValue()); @@ -404,7 +405,7 @@ public void buyUnitAppliesClanCostMultiplierToInfantryAlso() { assertTrue(quartermaster.buyUnit(mockEntity, 0)); // ...and the new clan infantry should be added to the campaign... - verify(mockCampaign, times(1)).addNewUnit(eq(mockEntity), eq(false), eq(0), eq(3)); + verify(mockCampaign, times(1)).addNewUnit(eq(mockEntity), eq(false), eq(0), eq(PartQuality.QUALITY_D)); // ...and it should cost the right amount. assertEquals(Money.of(clanMultiplier * cost), captor.getValue()); diff --git a/MekHQ/unittests/mekhq/campaign/unit/UnitTestUtilities.java b/MekHQ/unittests/mekhq/campaign/unit/UnitTestUtilities.java index cf4be42478..76542e2443 100644 --- a/MekHQ/unittests/mekhq/campaign/unit/UnitTestUtilities.java +++ b/MekHQ/unittests/mekhq/campaign/unit/UnitTestUtilities.java @@ -26,6 +26,7 @@ import megamek.common.util.BuildingBlock; import mekhq.TestUtilities; import mekhq.campaign.Campaign; +import mekhq.campaign.parts.enums.PartQuality; import java.io.ByteArrayInputStream; import java.io.InputStream; @@ -35,7 +36,7 @@ public final class UnitTestUtilities { public static Unit addAndGetUnit(Campaign campaign, Entity entity) { - campaign.addNewUnit(entity, false, 0, 3); + campaign.addNewUnit(entity, false, 0, PartQuality.QUALITY_D); for (Unit unit : campaign.getHangar().getUnits()) { return unit; } From 879dd07fc15453c860ea5ce9a40e0a6a5697d88d Mon Sep 17 00:00:00 2001 From: Weaver Date: Wed, 16 Oct 2024 09:30:40 -0700 Subject: [PATCH 023/118] Make SkillType functions nicer and clean up code I touch --- MekHQ/src/mekhq/campaign/parts/Armor.java | 58 ++++---- .../src/mekhq/campaign/parts/MekLocation.java | 61 ++++----- .../src/mekhq/campaign/parts/MissingPart.java | 48 +++---- MekHQ/src/mekhq/campaign/parts/Part.java | 94 ++++++------- MekHQ/src/mekhq/campaign/parts/PodSpace.java | 49 +++---- .../campaign/parts/ProtoMekLocation.java | 62 ++++----- .../mekhq/campaign/personnel/SkillType.java | 126 +++++++----------- 7 files changed, 210 insertions(+), 288 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/Armor.java b/MekHQ/src/mekhq/campaign/parts/Armor.java index 1e37aa9ff0..6cc9c10abe 100644 --- a/MekHQ/src/mekhq/campaign/parts/Armor.java +++ b/MekHQ/src/mekhq/campaign/parts/Armor.java @@ -45,6 +45,7 @@ import mekhq.campaign.work.IAcquisitionWork; import mekhq.campaign.work.WorkTime; import mekhq.utilities.MHQXMLUtility; +import mekhq.utilities.ReportingUtilities; /** * @author Jay Lawson (jaylawson39 at yahoo.com) @@ -122,39 +123,34 @@ public String getDesc() { if (isSalvaging()) { return super.getDesc(); } - String bonus = getAllMods(null).getValueAsString(); - if (getAllMods(null).getValue() > -1) { - bonus = '+' + bonus; - } - String toReturn = "Replace ") + .append(getName()); if (!getCampaign().getCampaignOptions().isDestroyByMargin()) { - toReturn += " - " - + SkillType.getExperienceLevelName(getSkillMin()) + '+' - + "
"; - } else { - toReturn += "

"; - } - - toReturn += getDetails() + "
"; - if (getAmountAvailable() > 0) { - toReturn += getTimeLeft() + " minutes" + scheduled; - toReturn += " TN: " + bonus; - } - if (getMode() != WorkTime.NORMAL) { - toReturn += " " + getCurrentModeName() + ""; + toReturn.append(" - ") + .append(ReportingUtilities.messageSurroundedBySpanWithColor( + SkillType.getExperienceLevelColor(getSkillMin()), + SkillType.getExperienceLevelName(getSkillMin()) + "+")); + } + toReturn.append("
") + .append(getDetails()) + .append("
"); + + if (getSkillMin() <= SkillType.EXP_ELITE) { + toReturn.append(getTimeLeft()) + .append(" minutes") + .append(null != getTech() ? " (scheduled)" : "") + .append(" TN: ") + .append(getAllMods(null).getValue() > -1 ? "+" : "") + .append(getAllMods(null).getValueAsString()); + if (getMode() != WorkTime.NORMAL) { + toReturn.append(" ") + .append(getCurrentModeName()) + .append( ""); + } } - toReturn += ""; - return toReturn; - + toReturn.append(""); + return toReturn.toString(); } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/MekLocation.java b/MekHQ/src/mekhq/campaign/parts/MekLocation.java index f4d361a559..1344aab013 100644 --- a/MekHQ/src/mekhq/campaign/parts/MekLocation.java +++ b/MekHQ/src/mekhq/campaign/parts/MekLocation.java @@ -36,7 +36,6 @@ import megamek.common.verifier.Structure; import megamek.common.verifier.TestEntity.Ceil; import megamek.logging.MMLogger; -import mekhq.MekHQ; import mekhq.campaign.Campaign; import mekhq.campaign.finances.Money; import mekhq.campaign.parts.enums.PartRepairType; @@ -46,6 +45,7 @@ import mekhq.campaign.unit.Unit; import mekhq.campaign.work.WorkTime; import mekhq.utilities.MHQXMLUtility; +import mekhq.utilities.ReportingUtilities; /** * @author Jay Lawson (jaylawson39 at yahoo.com) @@ -864,46 +864,35 @@ public String getDesc() { if ((!isBreached() && !isBlownOff()) || isSalvaging()) { return super.getDesc(); } - String toReturn = "") + .append(isBlownOff() ? "Re-attach " : "Seal ") + .append(getName()) + .append(" - ") + .append(ReportingUtilities.messageSurroundedBySpanWithColor( + SkillType.getExperienceLevelColor(getSkillMin()), + SkillType.getExperienceLevelName(getSkillMin()) + "+")) + .append("
") + .append(getDetails()) + .append("
"); - if (getSkillMin() > SkillType.EXP_ELITE) { - toReturn += " - Impossible"; - } else { - toReturn += " - " - + SkillType.getExperienceLevelName(getSkillMin()) + '+' - + "
"; - } - - toReturn += getDetails() + "
"; if (getSkillMin() <= SkillType.EXP_ELITE) { - toReturn += getTimeLeft() + " minutes" + scheduled; - if (isBlownOff()) { - String bonus = getAllMods(null).getValueAsString(); - if (getAllMods(null).getValue() > -1) { - bonus = '+' + bonus; - } - toReturn += " TN: " + bonus; - if (getMode() != WorkTime.NORMAL) { - toReturn += " " + getCurrentModeName() + ""; + toReturn.append(getTimeLeft()) + .append(" minutes") + .append(null != getTech() ? " (scheduled)" : ""); + if(isBlownOff()) { + toReturn.append(" TN: ") + .append(getAllMods(null).getValue() > -1 ? "+" : "") + .append(getAllMods(null).getValueAsString()); } + if (getMode() != WorkTime.NORMAL) { + toReturn.append(" ") + .append(getCurrentModeName()) + .append( ""); } } - toReturn += ""; - return toReturn; + toReturn.append(""); + return toReturn.toString(); } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/MissingPart.java b/MekHQ/src/mekhq/campaign/parts/MissingPart.java index be027903fb..fb8dd05b5d 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingPart.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingPart.java @@ -30,6 +30,7 @@ import mekhq.campaign.unit.Unit; import mekhq.campaign.work.IAcquisitionWork; import mekhq.campaign.work.WorkTime; +import mekhq.utilities.ReportingUtilities; import java.io.PrintWriter; @@ -82,37 +83,32 @@ public boolean isSamePartType(Part part) { @Override public String getDesc() { - String bonus = getAllMods(null).getValueAsString(); - if (getAllMods(null).getValue() > -1) { - bonus = '+' + bonus; - } - String toReturn = "Replace ") + .append(getName()) + .append(" - ") + .append(ReportingUtilities.messageSurroundedBySpanWithColor( + SkillType.getExperienceLevelColor(getSkillMin()), + SkillType.getExperienceLevelName(getSkillMin()) + "+")) + .append("
") + .append(getDetails()) + .append("
"); - if (getSkillMin() > SkillType.EXP_ELITE) { - toReturn += " - Impossible"; - } else { - toReturn += " - " - + SkillType.getExperienceLevelName(getSkillMin()) + '+' - + "
"; - } - - toReturn += getDetails() + "
"; if (getSkillMin() <= SkillType.EXP_ELITE) { - toReturn += getTimeLeft() + " minutes" + scheduled; - toReturn += " TN: " + bonus; + toReturn.append(getTimeLeft()) + .append(" minutes") + .append(null != getTech() ? " (scheduled)" : "") + .append(" TN: ") + .append(getAllMods(null).getValue() > -1 ? "+" : "") + .append(getAllMods(null).getValueAsString()); if (getMode() != WorkTime.NORMAL) { - toReturn += " " + getCurrentModeName() + ""; + toReturn.append(" ") + .append(getCurrentModeName()) + .append( ""); } } - toReturn += ""; - return toReturn; + toReturn.append(""); + return toReturn.toString(); } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/Part.java b/MekHQ/src/mekhq/campaign/parts/Part.java index 8a8191f63d..690f9ff72a 100644 --- a/MekHQ/src/mekhq/campaign/parts/Part.java +++ b/MekHQ/src/mekhq/campaign/parts/Part.java @@ -423,71 +423,65 @@ public void setHits(int hits) { @Override public String getDesc() { - String bonus = getAllMods(null).getValueAsString(); - if (getAllMods(null).getValue() > -1) { - bonus = '+' + bonus; - } - String toReturn = "") + .append(isSalvaging() ? "Salvage " : "Repair ") + .append(getName()); if (!getCampaign().getCampaignOptions().isDestroyByMargin()) { - toReturn += " - " - + SkillType.getExperienceLevelName(getSkillMin()) + '+' - + "
"; - } else { - toReturn += "
"; - } - - toReturn += getDetails() + "
"; - if (getSkillMin() > SkillType.EXP_ELITE) { - toReturn += "Impossible"; - } else { - toReturn += getTimeLeft() + " minutes" + scheduled; - toReturn += " TN: " + bonus; + toReturn.append(" - ") + .append(ReportingUtilities.messageSurroundedBySpanWithColor( + SkillType.getExperienceLevelColor(getSkillMin()), + SkillType.getExperienceLevelName(getSkillMin()) + "+")); + } + toReturn.append("
") + .append(getDetails()) + .append("
"); + + if (getSkillMin() <= SkillType.EXP_ELITE) { + toReturn.append(getTimeLeft()) + .append(" minutes") + .append(null != getTech() ? " (scheduled)" : "") + .append(" TN: ") + .append(getAllMods(null).getValue() > -1 ? "+" : "") + .append(getAllMods(null).getValueAsString()); if (getMode() != WorkTime.NORMAL) { - toReturn += " " + getCurrentModeName() + ""; + toReturn.append(" ") + .append(getCurrentModeName()) + .append( ""); } } - toReturn += ""; - return toReturn; + toReturn.append(""); + return toReturn.toString(); } public String getRepairDesc() { - String toReturn = ""; - if (needsFixing()) { - String scheduled = ""; - if (getTech() != null) { - scheduled = " (scheduled) "; - } - String bonus = getAllMods(null).getValueAsString(); - if (getAllMods(null).getValue() > -1) { - bonus = '+' + bonus; - } + StringBuilder toReturn = new StringBuilder(); - toReturn += getTimeLeft() + " minutes" + scheduled; + if (needsFixing()) { + toReturn.append("") + .append(getTimeLeft()) + .append(" minutes") + .append(null != getTech() ? " (scheduled) " : ""); if (!getCampaign().getCampaignOptions().isDestroyByMargin()) { - toReturn += ", " - + SkillType.getExperienceLevelName(getSkillMin()) + '+' - + ReportingUtilities.CLOSING_SPAN_TAG; + toReturn.append(" - ") + .append(ReportingUtilities.messageSurroundedBySpanWithColor( + SkillType.getExperienceLevelColor(getSkillMin()), + SkillType.getExperienceLevelName(getSkillMin()) + "+")) + .append(""); } - toReturn += ", TN: " + bonus; + toReturn.append(", TN: ") + .append(getAllMods(null).getValue() > -1 ? "+" : "") + .append(getAllMods(null).getValueAsString()); if (getMode() != WorkTime.NORMAL) { - toReturn += ", " + getCurrentModeName(); + toReturn.append(" ") + .append(getCurrentModeName()) + .append( ""); } + toReturn.append(""); } - return toReturn; + return toReturn.toString(); } public String getTechBaseName() { diff --git a/MekHQ/src/mekhq/campaign/parts/PodSpace.java b/MekHQ/src/mekhq/campaign/parts/PodSpace.java index cd48272373..c0fc645a43 100644 --- a/MekHQ/src/mekhq/campaign/parts/PodSpace.java +++ b/MekHQ/src/mekhq/campaign/parts/PodSpace.java @@ -30,6 +30,7 @@ import mekhq.campaign.personnel.SkillType; import mekhq.campaign.unit.Unit; import mekhq.campaign.work.IPartWork; +import mekhq.utilities.ReportingUtilities; import java.util.ArrayList; import java.util.List; @@ -365,38 +366,28 @@ public void setShorthandedMod(int i) { @Override public String getDesc() { - String bonus = getAllMods(null).getValueAsString(); - if (getAllMods(null).getValue() > -1) { - bonus = '+' + bonus; - } - String toReturn = " SkillType.EXP_ELITE) { - toReturn += " - Impossible"; - } else { - toReturn += " - " - + SkillType.getExperienceLevelName(getSkillMin()) + '+' - + "
"; - } + StringBuilder toReturn = new StringBuilder(); + toReturn.append("") + .append(isSalvaging() ? "Salvage " : "Replace ") + .append(getPartName()) + .append(" Equipment - ") + .append(ReportingUtilities.messageSurroundedBySpanWithColor( + SkillType.getExperienceLevelColor(getSkillMin()), + SkillType.getExperienceLevelName(getSkillMin()) + "+")) + .append("
") + .append(getDetails()) + .append("
"); - toReturn += getDetails() + "
"; if (getSkillMin() <= SkillType.EXP_ELITE) { - toReturn += getTimeLeft() + " minutes" + scheduled; - toReturn += " TN: " + bonus; + toReturn.append(getTimeLeft()) + .append(" minutes") + .append(getTech() != null ? " (scheduled)" : "") + .append(" TN: ") + .append(getAllMods(null).getValue() > -1 ? "+" : "") + .append(getAllMods(null).getValueAsString()); } - toReturn += ""; - return toReturn; + toReturn.append(""); + return toReturn.toString(); } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java b/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java index c6d9067bbc..29f4f13d61 100644 --- a/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java +++ b/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java @@ -35,7 +35,6 @@ import megamek.common.TechAdvancement; import megamek.common.annotations.Nullable; import megamek.logging.MMLogger; -import mekhq.MekHQ; import mekhq.campaign.Campaign; import mekhq.campaign.finances.Money; import mekhq.campaign.parts.enums.PartRepairType; @@ -43,6 +42,7 @@ import mekhq.campaign.personnel.SkillType; import mekhq.campaign.work.WorkTime; import mekhq.utilities.MHQXMLUtility; +import mekhq.utilities.ReportingUtilities; /** * @author Jay Lawson (jaylawson39 at yahoo.com) @@ -580,49 +580,37 @@ public String getDesc() { if ((!isBreached() && !isBlownOff()) || isSalvaging()) { return super.getDesc(); } - String toReturn = "") + .append(isBlownOff() ? "Re-attach " : "Seal ") + .append(getName()); if (!getCampaign().getCampaignOptions().isDestroyByMargin()) { - if (getSkillMin() > SkillType.EXP_ELITE) { - toReturn += " - Impossible
"; - } else { - toReturn += " - " - + SkillType.getExperienceLevelName(getSkillMin()) + '+' - + "
"; - } - } else { - toReturn += "
"; + toReturn.append(" - ") + .append(ReportingUtilities.messageSurroundedBySpanWithColor( + SkillType.getExperienceLevelColor(getSkillMin()), + SkillType.getExperienceLevelName(getSkillMin()) + "+")); } + toReturn.append("
") + .append(getDetails()) + .append("
"); - toReturn += getDetails() + "
"; if (getSkillMin() <= SkillType.EXP_ELITE) { - toReturn += getTimeLeft() + " minutes" + scheduled; - if (isBlownOff()) { - String bonus = getAllMods(null).getValueAsString(); - if (getAllMods(null).getValue() > -1) { - bonus = '+' + bonus; - } - toReturn += " TN: " + bonus; - if (getMode() != WorkTime.NORMAL) { - toReturn += " " + getCurrentModeName() + ""; + toReturn.append(getTimeLeft()) + .append(" minutes") + .append(null != getTech() ? " (scheduled)" : ""); + if(isBlownOff()) { + toReturn.append(" TN: ") + .append(getAllMods(null).getValue() > -1 ? "+" : "") + .append(getAllMods(null).getValueAsString()); } + if (getMode() != WorkTime.NORMAL) { + toReturn.append(" ") + .append(getCurrentModeName()) + .append( ""); } } - toReturn += ""; - return toReturn; + toReturn.append(""); + return toReturn.toString(); } @Override diff --git a/MekHQ/src/mekhq/campaign/personnel/SkillType.java b/MekHQ/src/mekhq/campaign/personnel/SkillType.java index 1238b88543..f877148be7 100644 --- a/MekHQ/src/mekhq/campaign/personnel/SkillType.java +++ b/MekHQ/src/mekhq/campaign/personnel/SkillType.java @@ -25,6 +25,8 @@ import megamek.common.enums.*; import mekhq.MekHQ; import mekhq.utilities.MHQXMLUtility; +import mekhq.utilities.ReportingUtilities; + import org.apache.commons.lang3.StringUtils; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -127,27 +129,29 @@ public class SkillType { public static final int EXP_VETERAN = 3; public static final int EXP_ELITE = 4; - /** + private String name; + private int target; + private boolean countUp; + private int greenLvl; + private int regLvl; + private int vetLvl; + private int eliteLvl; + private Integer[] costs; + + /** * @param level skill level integer to get name for * @return String skill name */ public static String getExperienceLevelName(int level) { - switch (level) { - case EXP_ULTRA_GREEN: - return "Ultra-Green"; - case EXP_GREEN: - return "Green"; - case EXP_REGULAR: - return "Regular"; - case EXP_VETERAN: - return "Veteran"; - case EXP_ELITE: - return "Elite"; - case -1: - return "Unknown"; - default: - return "Impossible"; - } + return switch (level) { + case EXP_ULTRA_GREEN -> "Ultra-Green"; + case EXP_GREEN -> "Green"; + case EXP_REGULAR -> "Regular"; + case EXP_VETERAN -> "Veteran"; + case EXP_ELITE -> "Elite"; + case -1 -> "Unknown"; + default -> "Impossible"; + }; } /** @@ -155,22 +159,15 @@ public static String getExperienceLevelName(int level) { * @return String hex code for a font tag */ public static String getExperienceLevelColor(int level) { - switch (level) { - case EXP_ULTRA_GREEN: - return MekHQ.getMHQOptions().getFontColorSkillUltraGreenHexColor(); - case EXP_GREEN: - return MekHQ.getMHQOptions().getFontColorSkillGreenHexColor(); - case EXP_REGULAR: - return MekHQ.getMHQOptions().getFontColorSkillRegularHexColor(); - case EXP_VETERAN: - return MekHQ.getMHQOptions().getFontColorSkillVeteranHexColor(); - case EXP_ELITE: - return MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); - case -1: - return ""; - default: - return ""; - } + return switch (level) { + case EXP_ULTRA_GREEN -> MekHQ.getMHQOptions().getFontColorSkillUltraGreenHexColor(); + case EXP_GREEN -> MekHQ.getMHQOptions().getFontColorSkillGreenHexColor(); + case EXP_REGULAR -> MekHQ.getMHQOptions().getFontColorSkillRegularHexColor(); + case EXP_VETERAN -> MekHQ.getMHQOptions().getFontColorSkillVeteranHexColor(); + case EXP_ELITE -> MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); + case -1 -> ""; + default -> ""; + }; } /** @@ -178,74 +175,45 @@ public static String getExperienceLevelColor(int level) { * @return String hex code for a font tag */ public static String getExperienceLevelColor(SkillLevel level) { - switch(level) { - case ULTRA_GREEN: - return MekHQ.getMHQOptions().getFontColorSkillUltraGreenHexColor(); - case GREEN: - return MekHQ.getMHQOptions().getFontColorSkillGreenHexColor(); - case REGULAR: - return MekHQ.getMHQOptions().getFontColorSkillRegularHexColor(); - case VETERAN: - return MekHQ.getMHQOptions().getFontColorSkillVeteranHexColor(); - case ELITE: - return MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); - case HEROIC: - return MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); - case LEGENDARY: - return MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); - default: - return ""; - } + return switch(level) { + case ULTRA_GREEN -> MekHQ.getMHQOptions().getFontColorSkillUltraGreenHexColor(); + case GREEN -> MekHQ.getMHQOptions().getFontColorSkillGreenHexColor(); + case REGULAR -> MekHQ.getMHQOptions().getFontColorSkillRegularHexColor(); + case VETERAN -> MekHQ.getMHQOptions().getFontColorSkillVeteranHexColor(); + case ELITE -> MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); + case HEROIC -> MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); + case LEGENDARY -> MekHQ.getMHQOptions().getFontColorSkillEliteHexColor(); + default -> ""; + }; } /** * @param level - skill level integer to get tagged name for * @return String "SkillName" or "Skillname" if no color exists - */ + */ public static String getColoredExperienceLevelName(int level) { if (getExperienceLevelColor(level) == "") { return getExperienceLevelName(level); } - - StringBuilder toReturn = new StringBuilder(64); - toReturn.append("") - .append(getExperienceLevelName(level)) - .append(""); - return toReturn.toString(); + + return ReportingUtilities.messageSurroundedBySpanWithColor( + getExperienceLevelColor(level), getExperienceLevelName(level)); } /** * @param level - SkillLevel enum to get tagged name for * @return String "SkillName" or "Skillname" if no color exists - */ + */ public static String getColoredExperienceLevelName(SkillLevel level) { if (getExperienceLevelColor(level) == "") { return level.toString(); } - StringBuilder toReturn = new StringBuilder(64); - toReturn.append("") - .append(level.toString()) - .append(""); - return toReturn.toString(); + return ReportingUtilities.messageSurroundedBySpanWithColor( + getExperienceLevelColor(level), level.toString()); } - - - private String name; - private int target; - private boolean countUp; - private int greenLvl; - private int regLvl; - private int vetLvl; - private int eliteLvl; - private Integer[] costs; - public static void setSkillTypes(Map skills) { // we are going to cycle through all skills in case ones have been added since // this hash From 25ca16af2150d1f4b6c638bfb21408085c179ffa Mon Sep 17 00:00:00 2001 From: algebro Date: Wed, 16 Oct 2024 12:42:12 -0400 Subject: [PATCH 024/118] implement camops contract market negotiation --- .../contractMarket/CamOpsContractMarket.java | 64 +++++++++---- .../mekhq/campaign/mission/AtBContract.java | 94 ++++++++++++++++++- 2 files changed, 137 insertions(+), 21 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java b/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java index 31199c9394..9758f72992 100644 --- a/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java +++ b/MekHQ/src/mekhq/campaign/market/contractMarket/CamOpsContractMarket.java @@ -89,10 +89,8 @@ public void generateContractOffers(Campaign campaign, boolean newCampaign) { @Override public double calculatePaymentMultiplier(Campaign campaign, AtBContract contract) { - Faction employer = contract.getEmployerFaction(); int reputationFactor = campaign.getReputation().getReputationFactor(); - ContractTerms terms = new ContractTerms(contract.getContractType(), employer, - reputationFactor, campaign.getLocalDate()); + ContractTerms terms = getContractTerms(campaign, contract); return terms.getEmploymentMultiplier() * terms.getOperationsTempoMultiplier() * reputationFactor; } @@ -101,6 +99,29 @@ public void checkForFollowup(Campaign campaign, AtBContract contract) { } + @Override + public void rerollClause(AtBContract contract, int clause, Campaign campaign) { + if (getRerollsUsed(contract, clause) > 0) { + // CamOps RAW only allows 1 negotiation attempt + return; + } + int negotiationSkill = findNegotiationSkill(campaign); + int ratingMod = campaign.getReputation().getReputationModifier(); + int margin = rollOpposedNegotiation(negotiationSkill, ratingMod); + int change = margin / 2; + ContractTerms terms = getContractTerms(campaign, contract); + + switch (clause) { + case CLAUSE_COMMAND -> setCommandRights(contract, terms, contract.getCommandRoll() + change); + case CLAUSE_SALVAGE -> setSalvageRights(contract, terms, contract.getSalvageRoll() + change); + case CLAUSE_SUPPORT -> setSupportRights(contract, terms, contract.getSupportRoll() + change); + case CLAUSE_TRANSPORT -> setTransportRights(contract, terms, contract.getTransportRoll() + change); + default -> throw new IllegalStateException("Unexpected clause when rerolling contract clause: " + clause); + } + clauseMods.get(contract.getId()).rerollsUsed[clause]++; + contract.calculateContract(campaign); + } + private HiringHallModifiers getHiringHallModifiers(Campaign campaign) { HiringHallModifiers modifiers; if (campaign.getFaction().isMercenary()) { @@ -127,7 +148,7 @@ private int rollNegotiation(int skill, int modifiers) { } private int rollOpposedNegotiation(int skill, int modifiers) { - return Compute.d6(2) + skill + modifiers - Compute.d6(2) + EMPLOYER_NEGOTIATION_SKILL_LEVEL; + return rollNegotiation(skill, modifiers) - Compute.d6(2) + EMPLOYER_NEGOTIATION_SKILL_LEVEL; } private int getNumberOfOffers(int margin) { @@ -162,8 +183,7 @@ private Optional generateContract(Campaign campaign, ReputationCont } // Step 2: Determine the mission type contract.setContractType(determineMission(campaign, employer, reputation.getReputationModifier())); - ContractTerms contractTerms = new ContractTerms(contract.getContractType(), - employer, reputation.getReputationFactor(), campaign.getLocalDate()); + ContractTerms contractTerms = getContractTerms(campaign, contract); setEnemyCode(contract); setAttacker(contract); // Step 3: Set the system location @@ -297,20 +317,28 @@ private AtBContractType determineMission(Campaign campaign, Faction employer, in } } + private ContractTerms getContractTerms(Campaign campaign, AtBContract contract) { + return new ContractTerms(contract.getContractType(), + contract.getEmployerFaction(), + campaign.getReputation().getReputationFactor(), + campaign.getLocalDate()); + } + private void setContractClauses(AtBContract contract, ContractTerms terms) { - setCommandRights(contract, terms); - setSalvageRights(contract, terms); - setSupportRights(contract, terms); - setTransportRights(contract, terms); + clauseMods.put(contract.getId(), new ClauseMods()); + setCommandRights(contract, terms, Compute.d6(2)); + setSalvageRights(contract, terms, Compute.d6(2)); + setSupportRights(contract, terms, Compute.d6(2)); + setTransportRights(contract, terms, Compute.d6(2)); } - private void setCommandRights(AtBContract contract, ContractTerms terms) { - int roll = Compute.d6(2); + private void setCommandRights(AtBContract contract, ContractTerms terms, int roll) { + contract.setCommandRoll(roll); contract.setCommandRights(terms.getCommandRights(roll)); } - private void setSalvageRights(AtBContract contract, ContractTerms terms) { - int roll = Compute.d6(2); + private void setSalvageRights(AtBContract contract, ContractTerms terms, int roll) { + contract.setSalvageRoll(roll); if (terms.isSalvageExchange(roll)) { contract.setSalvageExchange(true); } else { @@ -319,8 +347,8 @@ private void setSalvageRights(AtBContract contract, ContractTerms terms) { } } - private void setSupportRights(AtBContract contract, ContractTerms terms) { - int roll = Compute.d6(2); + private void setSupportRights(AtBContract contract, ContractTerms terms, int roll) { + contract.setSupportRoll(roll); if (terms.isStraightSupport(roll)) { contract.setStraightSupport(terms.getSupportPercentage(roll)); } else if (terms.isBattleLossComp(roll)) { @@ -330,8 +358,8 @@ private void setSupportRights(AtBContract contract, ContractTerms terms) { } } - private void setTransportRights(AtBContract contract, ContractTerms terms) { - int roll = Compute.d6(2); + private void setTransportRights(AtBContract contract, ContractTerms terms, int roll) { + contract.setTransportRoll(roll); contract.setTransportComp(terms.getTransportTerms(roll)); } diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 0fe041e108..d2165923a9 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -63,7 +63,6 @@ import javax.swing.*; import java.awt.*; -import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.nio.file.Files; @@ -84,6 +83,10 @@ import static megamek.common.enums.SkillLevel.REGULAR; import static megamek.common.enums.SkillLevel.parseFromInteger; import static megamek.common.enums.SkillLevel.parseFromString; +import static mekhq.campaign.market.contractMarket.AbstractContractMarket.CLAUSE_COMMAND; +import static mekhq.campaign.market.contractMarket.AbstractContractMarket.CLAUSE_SALVAGE; +import static mekhq.campaign.market.contractMarket.AbstractContractMarket.CLAUSE_SUPPORT; +import static mekhq.campaign.market.contractMarket.AbstractContractMarket.CLAUSE_TRANSPORT; import static mekhq.campaign.mission.AtBDynamicScenarioFactory.getEntity; import static mekhq.campaign.mission.BotForceRandomizer.UNIT_WEIGHT_UNSPECIFIED; import static mekhq.campaign.universe.Factions.getFactionLogo; @@ -174,6 +177,11 @@ public class AtBContract extends Contract { "mekhq.resources.AtBContract", MekHQ.getMHQOptions().getLocale()); + private int commandRoll; + private int salvageRoll; + private int supportRoll; + private int transportRoll; + protected AtBContract() { this(null); } @@ -243,7 +251,7 @@ public static Camouflage pickRandomCamouflage(int currentYear, String factionCod final String ROOT_DIRECTORY = "data/images/camo/"; String camouflageDirectory = getCamouflageDirectory(currentYear, factionCode); - + // Gather all files List allPaths = null; @@ -254,7 +262,7 @@ public static Camouflage pickRandomCamouflage(int currentYear, String factionCod } catch (IOException e) { logger.error("Error getting list of camouflages", e); } - + // Select a random file to set camouflage, if there are files available if ((null != allPaths) && (!allPaths.isEmpty())) { Path randomPath = allPaths.get(new Random().nextInt(allPaths.size())); @@ -1904,4 +1912,84 @@ private static int getAverageBattleValue(Campaign campaign, String factionCode, return totalBattleValue / rollingCount; } + + /** + * Get the command roll that was used to determine command rights. Only used by CamOps Contract + * Market. + * + * @return + */ + public int getCommandRoll() { + return commandRoll; + } + + /** + * Set the command roll that was used to determine command rights. Only used by CamOps Contract + * Market. + * + * @param roll + */ + public void setCommandRoll(int roll) { + commandRoll = roll; + } + + /** + * Get the salvage roll that was used to determine salvage rights. Only used by CamOps Contract + * Market. + * + * @return + */ + public int getSalvageRoll() { + return salvageRoll; + } + + /** + * Set the salvage roll that was used to determine salvage rights. Only used by CamOps Contract + * Market. + * + * @param roll + */ + public void setSalvageRoll(int roll) { + salvageRoll = roll; + } + + /** + * Get the support roll that was used to determine support rights. Only used by CamOps Contract + * Market. + * + * @return + */ + public int getSupportRoll() { + return supportRoll; + } + + /** + * Set the support roll that was used to determine support rights. Only used by CamOps Contract + * Market. + * + * @param roll + */ + public void setSupportRoll(int roll) { + supportRoll = roll; + } + + /** + * Get the transport roll that was used to determine transport rights. Only used by CamOps Contract + * Market. + * + * @return + */ + public int getTransportRoll() { + return transportRoll; + } + + /** + * Set the transport roll that was used to determine transport rights. Only used by CamOps Contract + * Market. + * + * @param roll + */ + public void setTransportRoll(int roll) { + transportRoll = roll; + } } From 7bd5b535ca175ecedbcfb80e05c0b535bae55906 Mon Sep 17 00:00:00 2001 From: algebro Date: Wed, 16 Oct 2024 12:47:46 -0400 Subject: [PATCH 025/118] remove unnecessary imports --- MekHQ/src/mekhq/campaign/mission/AtBContract.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index d2165923a9..4de7d22772 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -83,10 +83,6 @@ import static megamek.common.enums.SkillLevel.REGULAR; import static megamek.common.enums.SkillLevel.parseFromInteger; import static megamek.common.enums.SkillLevel.parseFromString; -import static mekhq.campaign.market.contractMarket.AbstractContractMarket.CLAUSE_COMMAND; -import static mekhq.campaign.market.contractMarket.AbstractContractMarket.CLAUSE_SALVAGE; -import static mekhq.campaign.market.contractMarket.AbstractContractMarket.CLAUSE_SUPPORT; -import static mekhq.campaign.market.contractMarket.AbstractContractMarket.CLAUSE_TRANSPORT; import static mekhq.campaign.mission.AtBDynamicScenarioFactory.getEntity; import static mekhq.campaign.mission.BotForceRandomizer.UNIT_WEIGHT_UNSPECIFIED; import static mekhq.campaign.universe.Factions.getFactionLogo; From f04a640b8110f38e718445faf936d07d851d41fa Mon Sep 17 00:00:00 2001 From: algebro Date: Wed, 16 Oct 2024 12:54:32 -0400 Subject: [PATCH 026/118] serialize initial contract terms rolls --- MekHQ/src/mekhq/campaign/mission/AtBContract.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 4de7d22772..5351c7dc5a 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -1009,6 +1009,10 @@ protected int writeToXMLBegin(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "priorLogisticsFailure", priorLogisticsFailure); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "battleTypeMod", battleTypeMod); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "nextWeekBattleTypeMod", nextWeekBattleTypeMod); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "commandRoll", commandRoll); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "salvageRoll", salvageRoll); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "supportRoll", supportRoll); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "transportRoll", transportRoll); if (parentContract != null) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "parentContractId", parentContract.getId()); @@ -1093,6 +1097,14 @@ public void loadFieldsFromXmlNode(Node wn) throws ParseException { battleTypeMod = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("nextWeekBattleTypeMod")) { nextWeekBattleTypeMod = Integer.parseInt(wn2.getTextContent()); + } else if (wn2.getNodeName().equalsIgnoreCase("commandRoll")) { + commandRoll = Integer.parseInt(wn2.getTextContent()); + } else if (wn2.getNodeName().equalsIgnoreCase("salvageRoll")) { + salvageRoll = Integer.parseInt(wn2.getTextContent()); + } else if (wn2.getNodeName().equalsIgnoreCase("supportRoll")) { + supportRoll = Integer.parseInt(wn2.getTextContent()); + } else if (wn2.getNodeName().equalsIgnoreCase("transportRoll")) { + transportRoll = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("specialEventScenarioDate")) { specialEventScenarioDate = MHQXMLUtility.parseDate(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("specialEventScenarioType")) { From 9cd836a43301424b2ca84466a041f53cb0762cbf Mon Sep 17 00:00:00 2001 From: Weaver Date: Wed, 16 Oct 2024 09:56:44 -0700 Subject: [PATCH 027/118] SkillType - proper string comparison and make javadoc happy --- MekHQ/src/mekhq/campaign/personnel/SkillType.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/SkillType.java b/MekHQ/src/mekhq/campaign/personnel/SkillType.java index f877148be7..1d1b4580b2 100644 --- a/MekHQ/src/mekhq/campaign/personnel/SkillType.java +++ b/MekHQ/src/mekhq/campaign/personnel/SkillType.java @@ -189,10 +189,10 @@ public static String getExperienceLevelColor(SkillLevel level) { /** * @param level - skill level integer to get tagged name for - * @return String "SkillName" or "Skillname" if no color exists + * @return "Skillname" wrapped by coloring span or bare if no color exists */ public static String getColoredExperienceLevelName(int level) { - if (getExperienceLevelColor(level) == "") { + if (getExperienceLevelColor(level).equals("")) { return getExperienceLevelName(level); } @@ -202,10 +202,10 @@ public static String getColoredExperienceLevelName(int level) { /** * @param level - SkillLevel enum to get tagged name for - * @return String "SkillName" or "Skillname" if no color exists + * @return "Skillname" wrapped by coloring span or bare if no color exists */ public static String getColoredExperienceLevelName(SkillLevel level) { - if (getExperienceLevelColor(level) == "") { + if (getExperienceLevelColor(level).equals("")) { return level.toString(); } From 1a100c9fe7c762fdbb3a2c100149efa2e206318b Mon Sep 17 00:00:00 2001 From: Weaver Date: Wed, 16 Oct 2024 10:06:36 -0700 Subject: [PATCH 028/118] SkillType - fix mediocre empty string check --- MekHQ/src/mekhq/campaign/personnel/SkillType.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/SkillType.java b/MekHQ/src/mekhq/campaign/personnel/SkillType.java index 1d1b4580b2..1d0262de95 100644 --- a/MekHQ/src/mekhq/campaign/personnel/SkillType.java +++ b/MekHQ/src/mekhq/campaign/personnel/SkillType.java @@ -192,7 +192,7 @@ public static String getExperienceLevelColor(SkillLevel level) { * @return "Skillname" wrapped by coloring span or bare if no color exists */ public static String getColoredExperienceLevelName(int level) { - if (getExperienceLevelColor(level).equals("")) { + if (getExperienceLevelColor(level).isEmpty()) { return getExperienceLevelName(level); } @@ -205,7 +205,7 @@ public static String getColoredExperienceLevelName(int level) { * @return "Skillname" wrapped by coloring span or bare if no color exists */ public static String getColoredExperienceLevelName(SkillLevel level) { - if (getExperienceLevelColor(level).equals("")) { + if (getExperienceLevelColor(level).isEmpty()) { return level.toString(); } From b9491acee03bc83619442165e17615b69ddded0e Mon Sep 17 00:00:00 2001 From: Weaver Date: Wed, 16 Oct 2024 11:51:50 -0700 Subject: [PATCH 029/118] Skill Colors - Better Default Colors --- MekHQ/src/mekhq/MHQOptions.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MekHQ/src/mekhq/MHQOptions.java b/MekHQ/src/mekhq/MHQOptions.java index 0f149a2cfe..186f546d42 100644 --- a/MekHQ/src/mekhq/MHQOptions.java +++ b/MekHQ/src/mekhq/MHQOptions.java @@ -611,7 +611,7 @@ public void setFontColorWarning(Color value) { public Color getFontColorSkillUltraGreen() { return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_ULTRAGREEN, - 0x42DFDF)); + 0xDF5341)); } /** @@ -627,7 +627,7 @@ public void setFontColorSkillUltraGreen(Color value) { public Color getFontColorSkillGreen() { return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_GREEN, - 0x43CF43)); + 0xCFAB43)); } /** @@ -643,7 +643,7 @@ public void setFontColorSkillGreen(Color value) { public Color getFontColorSkillRegular() { return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_REGULAR, - 0xCF9F43)); + 0x7FCF43)); } /** @@ -659,7 +659,7 @@ public void setFontColorSkillRegular(Color value) { public Color getFontColorSkillVeteran() { return new Color(userPreferences.node(MHQConstants.DISPLAY_NODE).getInt(MHQConstants.FONT_COLOR_SKILL_VETERAN, - 0xE85C5C)); + 0x5CE8E0)); } /** From e72f7fd47f0f839714b35b03d94a945864f0e34b Mon Sep 17 00:00:00 2001 From: Weaver Date: Wed, 16 Oct 2024 15:09:04 -0700 Subject: [PATCH 030/118] Parts In Use - finishing up the options --- MekHQ/src/mekhq/campaign/Campaign.java | 10 ++++---- .../mekhq/gui/dialog/PartsReportDialog.java | 24 +++++++++++-------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index e794c06a79..87e02c8177 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -2240,7 +2240,7 @@ private PartInUse getPartInUse(Part p) { } private void updatePartInUseData(PartInUse piu, Part p, boolean ignoreMothballedUnits, - int ignoreSparesUnderQuality) { + PartQuality ignoreSparesUnderQuality) { if (ignoreMothballedUnits && (null != p.getUnit()) && p.getUnit().isMothballed()) { // Ignore it @@ -2248,7 +2248,7 @@ private void updatePartInUseData(PartInUse piu, Part p, boolean ignoreMothballed piu.setUseCount(piu.getUseCount() + getQuantity(p)); } else { if (p.isPresent()) { - if (p.getQuality() < ignoreSparesUnderQuality) { + if (p.getQuality().toNumeric() < ignoreSparesUnderQuality.toNumeric()) { // Ignore it } else { piu.setStoreCount(piu.getStoreCount() + getQuantity(p)); @@ -2261,7 +2261,8 @@ private void updatePartInUseData(PartInUse piu, Part p, boolean ignoreMothballed } /** Update the piu with the current campaign data */ - public void updatePartInUse(PartInUse piu, boolean ignoreMothballedUnits, int ignoreSparesUnderQuality) { + public void updatePartInUse(PartInUse piu, boolean ignoreMothballedUnits, + PartQuality ignoreSparesUnderQuality) { piu.setUseCount(0); piu.setStoreCount(0); piu.setTransferCount(0); @@ -2282,7 +2283,8 @@ public void updatePartInUse(PartInUse piu, boolean ignoreMothballedUnits, int ig } } - public Set getPartsInUse(boolean ignoreMothballedUnits, int ignoreSparesUnderQuality) { + public Set getPartsInUse(boolean ignoreMothballedUnits, + PartQuality ignoreSparesUnderQuality) { // java.util.Set doesn't supply a get(Object) method, so we have to use a // java.util.Map Map inUse = new HashMap<>(); diff --git a/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java b/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java index 1e1a8f41a3..63f940a55b 100644 --- a/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java @@ -32,7 +32,6 @@ import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.GroupLayout; -import javax.swing.GroupLayout.Alignment; import static javax.swing.GroupLayout.Alignment; import javax.swing.JDialog; @@ -51,6 +50,7 @@ import mekhq.campaign.Quartermaster; import mekhq.campaign.parts.Part; import mekhq.campaign.parts.PartInUse; +import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.work.IAcquisitionWork; import mekhq.gui.CampaignGUI; import mekhq.gui.model.PartsInUseTableModel; @@ -264,12 +264,16 @@ public void actionPerformed(ActionEvent e) { ignoreMothballedCheck = new JCheckBox(resourceMap.getString("chkIgnoreMothballed.text")); ignoreMothballedCheck.addActionListener(evt -> refreshOverviewPartsInUse()); - String[] qualities; - if(!campaign.getCampaignOptions().isReverseQualityNames()) { - qualities = new String[] {" ", "B", "C", "D", "E", "F"}; - } else { - qualities = new String[] {" ", "E", "D", "C", "B", "A"}; - } + boolean reverse = campaign.getCampaignOptions().isReverseQualityNames(); + String[] qualities = { + " ", // Combo box is blank for first one because it accepts everything and is default + PartQuality.QUALITY_B.toName(reverse), + PartQuality.QUALITY_C.toName(reverse), + PartQuality.QUALITY_D.toName(reverse), + PartQuality.QUALITY_E.toName(reverse), + PartQuality.QUALITY_F.toName(reverse) + }; + ignoreSparesUnderQualityCB = new JComboBox(qualities); ignoreSparesUnderQualityCB.setMaximumSize(ignoreSparesUnderQualityCB.getPreferredSize()); ignoreSparesUnderQualityCB.addActionListener(evt -> refreshOverviewPartsInUse()); @@ -307,12 +311,12 @@ public void actionPerformed(ActionEvent e) { * @return minimum internal quality level to use */ - private int getMinimumQuality(String rating) { + private PartQuality getMinimumQuality(String rating) { if (rating.equals(" ")) { // The blank spot always means "everything", so minimum = lowest - return Part.QUALITY_A; + return PartQuality.QUALITY_A; } else { - return Part.getQualityFromName(rating, campaign.getCampaignOptions().isReverseQualityNames()); + return PartQuality.fromName(rating, campaign.getCampaignOptions().isReverseQualityNames()); } } From 889e3c6bb457abca23480f836a884307e9652564 Mon Sep 17 00:00:00 2001 From: Weaver Date: Thu, 17 Oct 2024 08:41:49 -0700 Subject: [PATCH 031/118] Show Tech SPAs on Tech Panel in Repair Tab --- MekHQ/src/mekhq/campaign/parts/Refit.java | 5 +++-- MekHQ/src/mekhq/gui/model/TechTableModel.java | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/Refit.java b/MekHQ/src/mekhq/campaign/parts/Refit.java index 6a957348b1..5f447df84a 100644 --- a/MekHQ/src/mekhq/campaign/parts/Refit.java +++ b/MekHQ/src/mekhq/campaign/parts/Refit.java @@ -57,6 +57,7 @@ import mekhq.campaign.parts.equipment.LargeCraftAmmoBin; import mekhq.campaign.parts.equipment.MissingEquipmentPart; import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.PersonnelOptions; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.unit.Unit; import mekhq.campaign.unit.cleanup.EquipmentUnscrambler; @@ -1768,8 +1769,8 @@ public TargetRoll getAllMods(Person tech) { mods.addModifier(2, "custom job"); } - if ((null != tech) && tech.getOptions().booleanOption("tech_engineer")) { - mods.addModifier(-2, "engineer"); + if ((null != tech) && tech.getOptions().booleanOption(PersonnelOptions.TECH_ENGINEER)) { + mods.addModifier(-2, "Engineer"); } return mods; } diff --git a/MekHQ/src/mekhq/gui/model/TechTableModel.java b/MekHQ/src/mekhq/gui/model/TechTableModel.java index f5533e2928..5b5863cfde 100644 --- a/MekHQ/src/mekhq/gui/model/TechTableModel.java +++ b/MekHQ/src/mekhq/gui/model/TechTableModel.java @@ -139,6 +139,26 @@ public String getTechDesc(Person tech, boolean overtimeAllowed, IPartWork part) if (overtimeAllowed) { toReturn.append(String.format(" + (%d overtime)", tech.getOvertimeLeft())); } + + if (tech.getOptions().booleanOption(PersonnelOptions.TECH_ENGINEER)) { + toReturn.append(", Engineer"); + } + if (tech.getOptions().booleanOption(PersonnelOptions.TECH_MAINTAINER)) { + toReturn.append(", Maintainer"); + } + if (tech.getOptions().booleanOption(PersonnelOptions.TECH_FIXER)) { + toReturn.append(", Mr/Ms Fix-it"); + } + if (tech.getOptions().booleanOption(PersonnelOptions.TECH_ARMOR_SPECIALIST)) { + toReturn.append(", Armor Specialist"); + } + if (tech.getOptions().booleanOption(PersonnelOptions.TECH_INTERNAL_SPECIALIST)) { + toReturn.append(", Internal Specialist"); + } + if (tech.getOptions().booleanOption(PersonnelOptions.TECH_WEAPON_SPECIALIST)) { + toReturn.append(", Weapon Specialist"); + } + toReturn.append(""); return toReturn.toString(); } From aee40b3704c5fc349da96b17f0f7b8b26fd7677d Mon Sep 17 00:00:00 2001 From: Weaver Date: Thu, 17 Oct 2024 13:27:49 -0700 Subject: [PATCH 032/118] Fix tank turrets missing their unitTonnage --- MekHQ/src/mekhq/campaign/market/PartsStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/market/PartsStore.java b/MekHQ/src/mekhq/campaign/market/PartsStore.java index 784b99e626..9fc743f925 100644 --- a/MekHQ/src/mekhq/campaign/market/PartsStore.java +++ b/MekHQ/src/mekhq/campaign/market/PartsStore.java @@ -429,7 +429,7 @@ private void stockVeeComponents(Campaign c) { parts.add(new VeeStabilizer(0, -1, c)); for (int ton = 5; ton <= 100; ton = ton + 5) { parts.add(new Rotor(ton, c)); - parts.add(new Turret(ton, -1, c)); + parts.add(new Turret(ton, ton, c)); } } From 7ea1fa6b3d2d882ea69ab9c95406f44bf7749ce2 Mon Sep 17 00:00:00 2001 From: Weaver Date: Thu, 17 Oct 2024 13:37:07 -0700 Subject: [PATCH 033/118] Fix supercharger omnipod price. --- MekHQ/src/mekhq/campaign/parts/equipment/MASC.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java b/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java index f3657a0964..a1d9505b3a 100644 --- a/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java +++ b/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java @@ -81,7 +81,7 @@ private double calculateTonnage() { @Override public Money getStickerPrice() { if (isSupercharger()) { - return Money.of(engineRating * (isOmniPodded() ? 1250 : 10000)); + return Money.of(engineRating * (isOmniPodded() ? 12500 : 10000)); } else { return Money.of(engineRating * getTonnage() * 1000); } From 79029a86e047c72908b3d7ce3b32a2aabe94d8a1 Mon Sep 17 00:00:00 2001 From: Weaver Date: Thu, 17 Oct 2024 16:28:05 -0700 Subject: [PATCH 034/118] Empty omnipods show the tech type their item is for --- MekHQ/src/mekhq/campaign/parts/OmniPod.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MekHQ/src/mekhq/campaign/parts/OmniPod.java b/MekHQ/src/mekhq/campaign/parts/OmniPod.java index 57a35a2c49..da1cab5c7d 100644 --- a/MekHQ/src/mekhq/campaign/parts/OmniPod.java +++ b/MekHQ/src/mekhq/campaign/parts/OmniPod.java @@ -68,6 +68,15 @@ public OmniPod(Part partType, Campaign c) { name = "OmniPod"; } + @Override + public int getTechBase() { + if (null != partType) { + return partType.getTechBase(); + } else { + return TechAdvancement.TECH_BASE_ALL; + } + } + @Override public void setCampaign(Campaign c) { super.setCampaign(c); From b299c52839eb5921d53ee8d8536511f05a382f3e Mon Sep 17 00:00:00 2001 From: HammerGS Date: Thu, 17 Oct 2024 18:25:21 -0600 Subject: [PATCH 035/118] Update history.txt --- MekHQ/docs/history.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MekHQ/docs/history.txt b/MekHQ/docs/history.txt index 0b907323ad..f9ef169a7d 100644 --- a/MekHQ/docs/history.txt +++ b/MekHQ/docs/history.txt @@ -104,6 +104,9 @@ MEKHQ VERSION HISTORY: + PR #5034: New row highlights for Personnel Table: Gone, Absent, Fatigued + FIX #5028: Random Camo Allocation Fixes + PR #5036: Corrected Scenario Modifiers for HouseOfficer units ++ PR #5052: Convert the Part Quality concept into an enum ++ PR #5053: Colorise Skill Levels ++ PR #5054: Implement CamOps Contract Negotiation 0.50.0 (2024-09-01 2000 UTC) (THIS MARKS THE START OF JAVA 17 AS THE MINIMUM REQUIRED) + PR #4332: CI Updates for windows build and normalizing @@ -298,6 +301,9 @@ MEKHQ VERSION HISTORY: + PR #4758: MHQ side of fix for MHQ 4755: NPE when opfor has no faction code + FIX #4697: Refactor salary editing logic and prevent NPE + PR #4690: Corrected Experience Generation Logic and Refined Campaign Options GUI ++ PR #5052: Convert the Part Quality concept into an enum ++ PR #5053: Colorise Skill Levels ++ PR #5054: Implement CamOps Contract Negotiation 0.49.20 (2024-06-28 2100 UTC) (THIS IS THE LAST VERSION TO SUPPORT JAVA 11) + PR #4005: Code internals: DialogOptionsListener update From 0b4d2c415b63095525f867de8f918736183ff674 Mon Sep 17 00:00:00 2001 From: Weaver Date: Thu, 17 Oct 2024 17:44:37 -0700 Subject: [PATCH 036/118] Parts In Use - resolved all issues --- MekHQ/src/mekhq/campaign/Campaign.java | 103 +++++++++++------- .../mekhq/gui/dialog/PartsReportDialog.java | 6 +- 2 files changed, 65 insertions(+), 44 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 87e02c8177..ac638a50d4 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -2239,83 +2239,104 @@ private PartInUse getPartInUse(Part p) { return (null != result.getPartToBuy()) ? result : null; } - private void updatePartInUseData(PartInUse piu, Part p, boolean ignoreMothballedUnits, - PartQuality ignoreSparesUnderQuality) { + /** + * Add data from an actual part to a PartInUse data element + * @param partInUse part in use record to update + * @param incomingPart new part that needs to be added to this record + * @param ignoreMothballedUnits don't count parts in mothballed units + * @param ignoreSparesUnderQuality don't count spare parts lower than this quality + */ + private void updatePartInUseData(PartInUse partInUse, Part incomingPart, + boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { - if (ignoreMothballedUnits && (null != p.getUnit()) && p.getUnit().isMothballed()) { - // Ignore it - } else if ((p.getUnit() != null) || (p instanceof MissingPart)) { - piu.setUseCount(piu.getUseCount() + getQuantity(p)); + if (ignoreMothballedUnits && (null != incomingPart.getUnit()) && incomingPart.getUnit().isMothballed()) { + return; + } else if ((incomingPart.getUnit() != null) || (incomingPart instanceof MissingPart)) { + partInUse.setUseCount(partInUse.getUseCount() + getQuantity(incomingPart)); } else { - if (p.isPresent()) { - if (p.getQuality().toNumeric() < ignoreSparesUnderQuality.toNumeric()) { - // Ignore it + if (incomingPart.isPresent()) { + if (incomingPart.getQuality().toNumeric() < ignoreSparesUnderQuality.toNumeric()) { + return; } else { - piu.setStoreCount(piu.getStoreCount() + getQuantity(p)); - piu.addSpare(p); + partInUse.setStoreCount(partInUse.getStoreCount() + getQuantity(incomingPart)); + partInUse.addSpare(incomingPart); } } else { - piu.setTransferCount(piu.getTransferCount() + getQuantity(p)); + partInUse.setTransferCount(partInUse.getTransferCount() + getQuantity(incomingPart)); } } } - /** Update the piu with the current campaign data */ - public void updatePartInUse(PartInUse piu, boolean ignoreMothballedUnits, + /** + * Find all the parts that match this PartInUse and update their data + * @param partInUse part in use record to update + * @param ignoreMothballedUnits don't count parts in mothballed units + * @param ignoreSparesUnderQuality don't count spare parts lower than this quality + */ + public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { - piu.setUseCount(0); - piu.setStoreCount(0); - piu.setTransferCount(0); - piu.setPlannedCount(0); - getWarehouse().forEachPart(p -> { - PartInUse newPiu = getPartInUse(p); - if (piu.equals(newPiu)) { - updatePartInUseData(piu, p, ignoreMothballedUnits, ignoreSparesUnderQuality); + partInUse.setUseCount(0); + partInUse.setStoreCount(0); + partInUse.setTransferCount(0); + partInUse.setPlannedCount(0); + getWarehouse().forEachPart(incomingPart -> { + PartInUse newPiu = getPartInUse(incomingPart); + if (partInUse.equals(newPiu)) { + updatePartInUseData(partInUse, incomingPart, + ignoreMothballedUnits, ignoreSparesUnderQuality); } }); for (IAcquisitionWork maybePart : shoppingList.getPartList()) { PartInUse newPiu = getPartInUse((Part) maybePart); - if (piu.equals(newPiu)) { - piu.setPlannedCount(piu.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() - : (Part) maybePart) * maybePart.getQuantity()); + if (partInUse.equals(newPiu)) { + partInUse.setPlannedCount(partInUse.getPlannedCount() + + getQuantity((maybePart instanceof MissingPart) ? + ((MissingPart) maybePart).getNewPart() : + (Part) maybePart) * maybePart.getQuantity()); } } } + /** + * Create a data set detailing all the parts being used (or not) and their warehouse spares + * @param ignoreMothballedUnits don't count parts in mothballed units + * @param ignoreSparesUnderQuality don't count spare parts lower than this quality + * @return a Set of PartInUse data for display or inspection + */ public Set getPartsInUse(boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { // java.util.Set doesn't supply a get(Object) method, so we have to use a // java.util.Map Map inUse = new HashMap<>(); - getWarehouse().forEachPart(p -> { - PartInUse piu = getPartInUse(p); - if (null == piu) { + getWarehouse().forEachPart(incomingPart -> { + PartInUse partInUse = getPartInUse(incomingPart); + if (null == partInUse) { return; } - if (inUse.containsKey(piu)) { - piu = inUse.get(piu); + if (inUse.containsKey(partInUse)) { + partInUse = inUse.get(partInUse); } else { - inUse.put(piu, piu); + inUse.put(partInUse, partInUse); } - updatePartInUseData(piu, p, ignoreMothballedUnits, ignoreSparesUnderQuality); + updatePartInUseData(partInUse, incomingPart, ignoreMothballedUnits, ignoreSparesUnderQuality); }); for (IAcquisitionWork maybePart : shoppingList.getPartList()) { if (!(maybePart instanceof Part)) { continue; } - PartInUse piu = getPartInUse((Part) maybePart); - if (null == piu) { + PartInUse partInUse = getPartInUse((Part) maybePart); + if (null == partInUse) { continue; } - if (inUse.containsKey(piu)) { - piu = inUse.get(piu); + if (inUse.containsKey(partInUse)) { + partInUse = inUse.get(partInUse); } else { - inUse.put(piu, piu); + inUse.put(partInUse, partInUse); } - piu.setPlannedCount(piu.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() - : (Part) maybePart) * maybePart.getQuantity()); + partInUse.setPlannedCount(partInUse.getPlannedCount() + + getQuantity((maybePart instanceof MissingPart) ? + ((MissingPart) maybePart).getNewPart() : + (Part) maybePart) * maybePart.getQuantity()); } return inUse.keySet().stream() diff --git a/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java b/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java index 63f940a55b..19f1f1fcdb 100644 --- a/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/PartsReportDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 The MegaMek Team. All rights reserved. + * Copyright (c) 2020-2024 The MegaMek Team. All rights reserved. * * This file is part of MekHQ. * @@ -45,6 +45,7 @@ import javax.swing.table.TableColumnModel; import javax.swing.table.TableRowSorter; +import megamek.client.ui.swing.util.UIUtil; import mekhq.MekHQ; import mekhq.campaign.Campaign; import mekhq.campaign.Quartermaster; @@ -301,7 +302,7 @@ public void actionPerformed(ActionEvent e) { .addComponent(ignoreSparesUnderQualityCB) .addComponent(btnClose))); - setPreferredSize(new Dimension(1400, 1000)); + setPreferredSize(UIUtil.scaleForGUI(1400,1000)); } @@ -310,7 +311,6 @@ public void actionPerformed(ActionEvent e) { * @param rating String containing A to F or space, from combo box * @return minimum internal quality level to use */ - private PartQuality getMinimumQuality(String rating) { if (rating.equals(" ")) { // The blank spot always means "everything", so minimum = lowest From 04c6bb4c1c16cfbff6fbab82525a053923854dd2 Mon Sep 17 00:00:00 2001 From: HammerGS Date: Thu, 17 Oct 2024 18:55:13 -0600 Subject: [PATCH 037/118] Update history.txt --- MekHQ/docs/history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/MekHQ/docs/history.txt b/MekHQ/docs/history.txt index f9ef169a7d..0d9267b741 100644 --- a/MekHQ/docs/history.txt +++ b/MekHQ/docs/history.txt @@ -107,6 +107,7 @@ MEKHQ VERSION HISTORY: + PR #5052: Convert the Part Quality concept into an enum + PR #5053: Colorise Skill Levels + PR #5054: Implement CamOps Contract Negotiation ++ PR #5055: Parts In Use - Filter Mothballed and Spare Part Quality 0.50.0 (2024-09-01 2000 UTC) (THIS MARKS THE START OF JAVA 17 AS THE MINIMUM REQUIRED) + PR #4332: CI Updates for windows build and normalizing From e226f837950b5bf390df80e7722e12b206052a09 Mon Sep 17 00:00:00 2001 From: Weaver Date: Thu, 17 Oct 2024 19:13:07 -0700 Subject: [PATCH 038/118] Empty MISSING omnipods do the same + docs --- MekHQ/src/mekhq/campaign/parts/MissingOmniPod.java | 12 ++++++++++++ MekHQ/src/mekhq/campaign/parts/OmniPod.java | 3 +++ 2 files changed, 15 insertions(+) diff --git a/MekHQ/src/mekhq/campaign/parts/MissingOmniPod.java b/MekHQ/src/mekhq/campaign/parts/MissingOmniPod.java index 1d9253a8f1..ef281185e1 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingOmniPod.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingOmniPod.java @@ -76,6 +76,18 @@ public void setCampaign(Campaign c) { public Part getPartType() { return partType; } + + /** + * @return The tech base of the part the omnipod is meant to contain. + */ + @Override + public int getTechBase() { + if (null != partType) { + return partType.getTechBase(); + } else { + return TechAdvancement.TECH_BASE_ALL; + } + } /** * Exports class data to xml diff --git a/MekHQ/src/mekhq/campaign/parts/OmniPod.java b/MekHQ/src/mekhq/campaign/parts/OmniPod.java index da1cab5c7d..76f21a844c 100644 --- a/MekHQ/src/mekhq/campaign/parts/OmniPod.java +++ b/MekHQ/src/mekhq/campaign/parts/OmniPod.java @@ -68,6 +68,9 @@ public OmniPod(Part partType, Campaign c) { name = "OmniPod"; } + /** + * @return The tech base of the part the omnipod is meant to contain. + */ @Override public int getTechBase() { if (null != partType) { From ba401b0ea0a44a77113326795256de831a0b988b Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 10:53:12 -0700 Subject: [PATCH 039/118] Part getDetails and getDesc cleanup --- .../src/mekhq/campaign/parts/AeroSensor.java | 15 ++------ .../src/mekhq/campaign/parts/EnginePart.java | 16 ++------- .../campaign/parts/KFChargingSystem.java | 4 +-- .../src/mekhq/campaign/parts/KFDriveCoil.java | 4 +-- .../campaign/parts/KFDriveController.java | 3 +- .../campaign/parts/KFFieldInitiator.java | 4 +-- .../mekhq/campaign/parts/KFHeliumTank.java | 4 +-- .../src/mekhq/campaign/parts/MekActuator.java | 18 +--------- .../src/mekhq/campaign/parts/MekLocation.java | 36 +++++++++++-------- MekHQ/src/mekhq/campaign/parts/MekSensor.java | 15 +------- .../parts/MissingBattleArmorSuit.java | 11 +++--- .../parts/MissingKFChargingSystem.java | 1 + .../campaign/parts/MissingKFDriveCoil.java | 1 + .../parts/MissingKFDriveController.java | 1 + .../parts/MissingKFFieldInitiator.java | 1 + .../campaign/parts/MissingKFHeliumTank.java | 1 + .../campaign/parts/MissingMekActuator.java | 1 + .../campaign/parts/MissingMekLocation.java | 1 + .../campaign/parts/MissingMekSensor.java | 1 + .../src/mekhq/campaign/parts/MissingPart.java | 11 ++++-- MekHQ/src/mekhq/campaign/parts/Part.java | 36 +++++++++++++++---- .../campaign/parts/ProtoMekArmActuator.java | 1 + .../mekhq/campaign/parts/ProtoMekJumpJet.java | 1 + .../campaign/parts/ProtoMekLegActuator.java | 1 + .../campaign/parts/ProtoMekLocation.java | 6 ++++ .../mekhq/campaign/parts/ProtoMekSensor.java | 1 + MekHQ/src/mekhq/campaign/parts/Rotor.java | 1 + .../mekhq/campaign/parts/TankLocation.java | 17 ++++++--- MekHQ/src/mekhq/campaign/parts/Turret.java | 10 +----- .../campaign/parts/equipment/JumpJet.java | 1 + .../mekhq/campaign/parts/equipment/MASC.java | 13 ++++--- .../parts/equipment/MissingJumpJet.java | 1 + .../campaign/parts/equipment/MissingMASC.java | 5 +++ 33 files changed, 130 insertions(+), 113 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/AeroSensor.java b/MekHQ/src/mekhq/campaign/parts/AeroSensor.java index 297b886caf..422929ca5a 100644 --- a/MekHQ/src/mekhq/campaign/parts/AeroSensor.java +++ b/MekHQ/src/mekhq/campaign/parts/AeroSensor.java @@ -51,6 +51,7 @@ public AeroSensor(int tonnage, boolean lc, Campaign c) { super(tonnage, c); this.name = "Aerospace Sensors"; this.largeCraft = lc; + this.unitTonnageMatters = true; } @Override @@ -234,19 +235,7 @@ public String getDetails() { @Override public String getDetails(boolean includeRepairDetails) { - String dropper = ""; - if (largeCraft) { - dropper = " (spacecraft)"; - } - - String details = super.getDetails(includeRepairDetails); - if (!details.isEmpty()) { - details += ", "; - } - - details += getUnitTonnage() + " tons" + dropper; - - return details; + return super.getDetails(includeRepairDetails) + (largeCraft ? " (spacecraft)" : ""); } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/EnginePart.java b/MekHQ/src/mekhq/campaign/parts/EnginePart.java index c0c46d4857..ef586954e9 100644 --- a/MekHQ/src/mekhq/campaign/parts/EnginePart.java +++ b/MekHQ/src/mekhq/campaign/parts/EnginePart.java @@ -54,6 +54,7 @@ public EnginePart(int tonnage, Engine e, Campaign c, boolean hover) { this.engine = e; this.forHover = hover; this.name = engine.getEngineName() + " Engine"; + this.unitTonnageMatters = true; } @Override @@ -408,19 +409,8 @@ public String getDetails() { @Override public String getDetails(boolean includeRepairDetails) { String details = super.getDetails(includeRepairDetails); - if (null != unit) { - return details; - } - - if (!details.isEmpty()) { - details += ", "; - } - - String hvrString = ""; - if (forHover) { - hvrString = " (hover)"; - } - return details + getUnitTonnage() + " tons" + hvrString; + + return details + (forHover ? " (hover)" : ""); } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/KFChargingSystem.java b/MekHQ/src/mekhq/campaign/parts/KFChargingSystem.java index f4afaee899..415649507c 100644 --- a/MekHQ/src/mekhq/campaign/parts/KFChargingSystem.java +++ b/MekHQ/src/mekhq/campaign/parts/KFChargingSystem.java @@ -72,6 +72,7 @@ public KFChargingSystem(int tonnage, int coreType, int docks, Campaign c) { this.coreType = coreType; this.docks = docks; this.name = "K-F Charging System"; + this.unitTonnageMatters = true; } @Override @@ -255,8 +256,7 @@ public String getDetails(boolean includeRepairDetails) { if (!details.isEmpty()) { joiner.add(details); } - joiner.add(getUnitTonnage() + " tons") - .add(getDocks() + " collars"); + joiner.add(getDocks() + " collars"); return joiner.toString(); } diff --git a/MekHQ/src/mekhq/campaign/parts/KFDriveCoil.java b/MekHQ/src/mekhq/campaign/parts/KFDriveCoil.java index a825f4d418..50c0b883a9 100644 --- a/MekHQ/src/mekhq/campaign/parts/KFDriveCoil.java +++ b/MekHQ/src/mekhq/campaign/parts/KFDriveCoil.java @@ -72,6 +72,7 @@ public KFDriveCoil(int tonnage, int coreType, int docks, Campaign c) { this.coreType = coreType; this.docks = docks; this.name = "K-F Drive Coil"; + this.unitTonnageMatters = true; } @Override @@ -254,8 +255,7 @@ public String getDetails(boolean includeRepairDetails) { if (!details.isEmpty()) { joiner.add(details); } - joiner.add(getUnitTonnage() + " tons") - .add(getDocks() + " collars"); + joiner.add(getDocks() + " collars"); return joiner.toString(); } diff --git a/MekHQ/src/mekhq/campaign/parts/KFDriveController.java b/MekHQ/src/mekhq/campaign/parts/KFDriveController.java index b1465dd413..a9ec00a525 100644 --- a/MekHQ/src/mekhq/campaign/parts/KFDriveController.java +++ b/MekHQ/src/mekhq/campaign/parts/KFDriveController.java @@ -72,6 +72,7 @@ public KFDriveController(int tonnage, int coreType, int docks, Campaign c) { this.coreType = coreType; this.docks = docks; this.name = "K-F Drive Controller"; + this.unitTonnageMatters = true; } @Override @@ -255,7 +256,7 @@ public String getDetails(boolean includeRepairDetails) { if (!details.isEmpty()) { joiner.add(details); } - joiner.add(getUnitTonnage() + " tons").add(getDocks() + " collars"); + joiner.add(getDocks() + " collars"); return joiner.toString(); } diff --git a/MekHQ/src/mekhq/campaign/parts/KFFieldInitiator.java b/MekHQ/src/mekhq/campaign/parts/KFFieldInitiator.java index e7a0948a07..90ea350de1 100644 --- a/MekHQ/src/mekhq/campaign/parts/KFFieldInitiator.java +++ b/MekHQ/src/mekhq/campaign/parts/KFFieldInitiator.java @@ -72,6 +72,7 @@ public KFFieldInitiator(int tonnage, int coreType, int docks, Campaign c) { this.coreType = coreType; this.docks = docks; this.name = "K-F Field Initiator"; + this.unitTonnageMatters = true; } @Override @@ -257,8 +258,7 @@ public String getDetails(boolean includeRepairDetails) { if (!details.isEmpty()) { joiner.add(details); } - joiner.add(getUnitTonnage() + " tons") - .add(getDocks() + " collars"); + joiner.add(getDocks() + " collars"); return joiner.toString(); } diff --git a/MekHQ/src/mekhq/campaign/parts/KFHeliumTank.java b/MekHQ/src/mekhq/campaign/parts/KFHeliumTank.java index 4f76aa8bc6..bd7bc4a2f8 100644 --- a/MekHQ/src/mekhq/campaign/parts/KFHeliumTank.java +++ b/MekHQ/src/mekhq/campaign/parts/KFHeliumTank.java @@ -72,6 +72,7 @@ public KFHeliumTank(int tonnage, int coreType, int docks, Campaign c) { this.coreType = coreType; this.docks = docks; this.name = "K-F Helium Tank"; + this.unitTonnageMatters = true; } @Override @@ -259,8 +260,7 @@ public String getDetails(boolean includeRepairDetails) { if (!details.isEmpty()) { joiner.add(details); } - joiner.add(getUnitTonnage() + " tons") - .add(getDocks() + " collars"); + joiner.add(getDocks() + " collars"); return joiner.toString(); } diff --git a/MekHQ/src/mekhq/campaign/parts/MekActuator.java b/MekHQ/src/mekhq/campaign/parts/MekActuator.java index d839393454..c92bf19235 100644 --- a/MekHQ/src/mekhq/campaign/parts/MekActuator.java +++ b/MekHQ/src/mekhq/campaign/parts/MekActuator.java @@ -27,12 +27,10 @@ import mekhq.campaign.finances.Money; import mekhq.campaign.parts.enums.PartRepairType; import mekhq.campaign.personnel.SkillType; -import org.apache.commons.lang3.StringUtils; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.PrintWriter; -import java.util.StringJoiner; /** * @author Jay Lawson (jaylawson39 at yahoo.com) @@ -77,6 +75,7 @@ public MekActuator(int tonnage, int type, int loc, Campaign c) { Mek m = new BipedMek(); this.name = m.getSystemName(type) + " Actuator"; this.location = loc; + this.unitTonnageMatters = true; } @Override @@ -230,21 +229,6 @@ public String getDetails() { return getDetails(true); } - @Override - public String getDetails(boolean includeRepairDetails) { - if (null != unit) { - StringJoiner sj = new StringJoiner(", "); - if (!StringUtils.isEmpty(getLocationName())) { - sj.add(getLocationName()); - } - if (includeRepairDetails && campaign.getCampaignOptions().isPayForRepairs()) { - sj.add(getActualValue().multipliedBy(0.2).toAmountAndSymbolString() + " to repair"); - } - return sj.toString(); - } - return getUnitTonnage() + " tons"; - } - @Override public void updateConditionFromPart() { if (null != unit) { diff --git a/MekHQ/src/mekhq/campaign/parts/MekLocation.java b/MekHQ/src/mekhq/campaign/parts/MekLocation.java index 1344aab013..210f740b02 100644 --- a/MekHQ/src/mekhq/campaign/parts/MekLocation.java +++ b/MekHQ/src/mekhq/campaign/parts/MekLocation.java @@ -68,6 +68,7 @@ public class MekLocation extends Part { public MekLocation() { this(0, 0, 0, false, false, false, false, false, null); + this.unitTonnageMatters = true; } @Override @@ -109,6 +110,7 @@ public MekLocation(int loc, int tonnage, int structureType, boolean clan, this.sensors = sensors; this.lifeSupport = lifeSupport; this.breached = false; + this.unitTonnageMatters = true; // TODO : need to account for internal structure and myomer types // crap, no static report for location names? this.name = "Mek Location"; @@ -566,14 +568,9 @@ public String getDetails() { @Override public String getDetails(boolean includeRepairDetails) { - if (getUnit() != null) { - return getDetailsOnUnit(includeRepairDetails); - } + StringBuilder toReturn = new StringBuilder(); - String toReturn = getUnitTonnage() + " tons"; - if (includeRepairDetails) { - toReturn += " (" + Math.round(100 * getPercent()) + "%)"; - } + toReturn.append(super.getDetails(includeRepairDetails)); if (loc == Mek.LOC_HEAD) { StringJoiner components = new StringJoiner(", "); @@ -586,11 +583,23 @@ public String getDetails(boolean includeRepairDetails) { } if (components.length() > 0) { - toReturn += " [" + components + ']'; + toReturn.append(" [") + .append(components) + .append(']'); } } - return toReturn; + if (getUnit() != null) { + return getDetailsOnUnit(includeRepairDetails); + } + + if (includeRepairDetails && getPercent() < 1.0) { + toReturn.append(" (") + .append(Math.round(100 * getPercent())) + .append("%)"); + } + + return toReturn.toString(); } private String getDetailsOnUnit(boolean includeRepairDetails) { @@ -602,11 +611,8 @@ private String getDetailsOnUnit(boolean includeRepairDetails) { toReturn += " (Breached)"; } else if (onBadHipOrShoulder()) { toReturn += " (Bad Hip/Shoulder)"; - } else { - toReturn += " (" + Math.round(100 * getPercent()) + "%)"; } } - return toReturn; } @@ -861,14 +867,16 @@ public TargetRoll getAllMods(Person tech) { @Override public String getDesc() { - if ((!isBreached() && !isBlownOff()) || isSalvaging()) { + if ((!isBreached() && !isBlownOff())) { return super.getDesc(); } StringBuilder toReturn = new StringBuilder(); toReturn.append("") .append(isBlownOff() ? "Re-attach " : "Seal ") .append(getName()) - .append(" - ") + .append(", ") + .append(getTonnage()) + .append("ton - ") .append(ReportingUtilities.messageSurroundedBySpanWithColor( SkillType.getExperienceLevelColor(getSkillMin()), SkillType.getExperienceLevelName(getSkillMin()) + "+")) diff --git a/MekHQ/src/mekhq/campaign/parts/MekSensor.java b/MekHQ/src/mekhq/campaign/parts/MekSensor.java index bb9c3b273b..7c3506a20e 100644 --- a/MekHQ/src/mekhq/campaign/parts/MekSensor.java +++ b/MekHQ/src/mekhq/campaign/parts/MekSensor.java @@ -47,6 +47,7 @@ public MekSensor() { public MekSensor(int tonnage, Campaign c) { super(tonnage, c); this.name = "Mek Sensors"; + this.unitTonnageMatters = true; } @Override @@ -221,20 +222,6 @@ public boolean isMountedOnDestroyedLocation() { return false; } - @Override - public String getDetails() { - return getDetails(true); - } - - @Override - public String getDetails(boolean includeRepairDetails) { - String details = super.getDetails(includeRepairDetails); - if (!details.isEmpty()) { - details += ", "; - } - return details + getUnitTonnage() + " tons"; - } - @Override public boolean isPartForEquipmentNum(int index, int loc) { return false; diff --git a/MekHQ/src/mekhq/campaign/parts/MissingBattleArmorSuit.java b/MekHQ/src/mekhq/campaign/parts/MissingBattleArmorSuit.java index d59fa32567..cfc35eeca7 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingBattleArmorSuit.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingBattleArmorSuit.java @@ -259,12 +259,13 @@ public String getDetails() { @Override public String getDetails(boolean includeRepairDetails) { - if (null == unit) { - return super.getDetails(includeRepairDetails); - + StringBuilder toReturn = new StringBuilder(); + if (null != unit) { + toReturn.append(unit.getEntity().getLocationName(trooper)) + .append("
"); } - String toReturn = unit.getEntity().getLocationName(trooper) + "
"; - return toReturn + super.getDetails(includeRepairDetails); + toReturn.append(super.getDetails(includeRepairDetails)); + return toReturn.toString(); } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/MissingKFChargingSystem.java b/MekHQ/src/mekhq/campaign/parts/MissingKFChargingSystem.java index 2164f0fc26..53aad8e294 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingKFChargingSystem.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingKFChargingSystem.java @@ -57,6 +57,7 @@ public MissingKFChargingSystem(int tonnage, int coreType, int docks, Campaign c) this.coreType = coreType; this.docks = docks; this.name = "K-F Charging System"; + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/MissingKFDriveCoil.java b/MekHQ/src/mekhq/campaign/parts/MissingKFDriveCoil.java index 0974d8f8d1..b98af1e52e 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingKFDriveCoil.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingKFDriveCoil.java @@ -57,6 +57,7 @@ public MissingKFDriveCoil(int tonnage, int coreType, int docks, Campaign c) { this.coreType = coreType; this.docks = docks; this.name = "K-F Drive Coil"; + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/MissingKFDriveController.java b/MekHQ/src/mekhq/campaign/parts/MissingKFDriveController.java index f788fb1033..6eb3602e6a 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingKFDriveController.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingKFDriveController.java @@ -57,6 +57,7 @@ public MissingKFDriveController(int tonnage, int coreType, int docks, Campaign c this.coreType = coreType; this.docks = docks; this.name = "K-F Drive Controller"; + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/MissingKFFieldInitiator.java b/MekHQ/src/mekhq/campaign/parts/MissingKFFieldInitiator.java index 208a2c0d20..e969ac1fe2 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingKFFieldInitiator.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingKFFieldInitiator.java @@ -59,6 +59,7 @@ public MissingKFFieldInitiator(int tonnage, int coreType, int docks, Campaign c) this.coreType = coreType; this.docks = docks; this.name = "K-F Field Initiator"; + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/MissingKFHeliumTank.java b/MekHQ/src/mekhq/campaign/parts/MissingKFHeliumTank.java index a1415536a7..cefdfc5f86 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingKFHeliumTank.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingKFHeliumTank.java @@ -58,6 +58,7 @@ public MissingKFHeliumTank(int tonnage, int coreType, int docks, Campaign c) { this.coreType = coreType; this.docks = docks; this.name = "K-F Helium Tank"; + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/MissingMekActuator.java b/MekHQ/src/mekhq/campaign/parts/MissingMekActuator.java index ba2eaa5496..acfea935f7 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingMekActuator.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingMekActuator.java @@ -62,6 +62,7 @@ public MissingMekActuator(int tonnage, int type, int loc, Campaign c) { Mek m = new BipedMek(); this.name = m.getSystemName(type) + " Actuator"; this.location = loc; + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/MissingMekLocation.java b/MekHQ/src/mekhq/campaign/parts/MissingMekLocation.java index f32df95ead..96d1eb6600 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingMekLocation.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingMekLocation.java @@ -80,6 +80,7 @@ public MissingMekLocation(int loc, int tonnage, int structureType, boolean clan, this.tsm = hasTSM; this.percent = 1.0; this.forQuad = quad; + this.unitTonnageMatters = true; // TODO: need to account for internal structure and myomer types // crap, no static report for location names? this.name = "Mek Location"; diff --git a/MekHQ/src/mekhq/campaign/parts/MissingMekSensor.java b/MekHQ/src/mekhq/campaign/parts/MissingMekSensor.java index f7d900f512..1c72560a55 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingMekSensor.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingMekSensor.java @@ -41,6 +41,7 @@ public MissingMekSensor() { public MissingMekSensor(int tonnage, Campaign c) { super(tonnage, c); this.name = resources.getString("MissingMekSensor.title"); + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/MissingPart.java b/MekHQ/src/mekhq/campaign/parts/MissingPart.java index fb8dd05b5d..dbd1e504e0 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingPart.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingPart.java @@ -84,9 +84,14 @@ public boolean isSamePartType(Part part) { @Override public String getDesc() { StringBuilder toReturn = new StringBuilder(); - toReturn.append("Replace ") - .append(getName()) - .append(" - ") + toReturn.append("Replace ") + .append(getName()); + if(isUnitTonnageMatters()) { + toReturn.append(" (") + .append(getUnitTonnage()) + .append(" ton)"); + } + toReturn.append(" - ") .append(ReportingUtilities.messageSurroundedBySpanWithColor( SkillType.getExperienceLevelColor(getSkillMin()), SkillType.getExperienceLevelName(getSkillMin()) + "+")) diff --git a/MekHQ/src/mekhq/campaign/parts/Part.java b/MekHQ/src/mekhq/campaign/parts/Part.java index 690f9ff72a..44ea6d2734 100644 --- a/MekHQ/src/mekhq/campaign/parts/Part.java +++ b/MekHQ/src/mekhq/campaign/parts/Part.java @@ -98,10 +98,16 @@ public abstract class Part implements IPartWork, ITechnology { protected String name; protected int id; - // this is the unitTonnage which needs to be tracked for some parts - // even when off the unit. actual tonnage is returned via the - // getTonnage() method + /** + * This is the unitTonnage which needs to be tracked for some parts + * even when off the unit. Actual tonnage is returned via the + * getTonnage() method + */ protected int unitTonnage; + /** + * Is this part's unitTonnage something that differentiates it from other parts + */ + protected boolean unitTonnageMatters; protected boolean omniPodded; @@ -185,6 +191,7 @@ public Part(int tonnage, Campaign c) { public Part(int tonnage, boolean omniPodded, Campaign c) { this.name = "Unknown"; this.unitTonnage = tonnage; + this.unitTonnageMatters = false; this.omniPodded = omniPodded; this.hits = 0; this.skillMin = SkillType.EXP_GREEN; @@ -354,6 +361,13 @@ public void setReplacementPart(@Nullable Part part) { public int getUnitTonnage() { return unitTonnage; } + + /** + * @return Is this an item that exists in multiple forms for units of different tonnages? + */ + public boolean isUnitTonnageMatters() { + return unitTonnageMatters; + } public abstract double getTonnage(); @@ -427,6 +441,11 @@ public String getDesc() { toReturn.append("") .append(isSalvaging() ? "Salvage " : "Repair ") .append(getName()); + if(isUnitTonnageMatters()) { + toReturn.append(" (") + .append(getUnitTonnage()) + .append(" ton)"); + } if (!getCampaign().getCampaignOptions().isDestroyByMargin()) { toReturn.append(" - ") .append(ReportingUtilities.messageSurroundedBySpanWithColor( @@ -1123,9 +1142,14 @@ public String getDetails(boolean includeRepairDetails) { sj.add("OmniPod"); } - if (includeRepairDetails) { - sj.add(hits + " hit(s)"); - if (campaign.getCampaignOptions().isPayForRepairs() && (hits > 0)) { + if (isUnitTonnageMatters()) + { + sj.add(getUnitTonnage() + " tons"); + } + + if (includeRepairDetails && hits > 0) { + sj.add(hits + (hits == 1 ? " hit" : " hits")); + if (campaign.getCampaignOptions().isPayForRepairs()) { sj.add(getActualValue().multipliedBy(0.2).toAmountAndSymbolString() + " to repair"); } } diff --git a/MekHQ/src/mekhq/campaign/parts/ProtoMekArmActuator.java b/MekHQ/src/mekhq/campaign/parts/ProtoMekArmActuator.java index b2de95c711..eaaf58d98d 100644 --- a/MekHQ/src/mekhq/campaign/parts/ProtoMekArmActuator.java +++ b/MekHQ/src/mekhq/campaign/parts/ProtoMekArmActuator.java @@ -65,6 +65,7 @@ public ProtoMekArmActuator(int tonnage, int loc, Campaign c) { super(tonnage, c); this.name = "ProtoMek Arm Actuator"; this.location = loc; + this.unitTonnageMatters = true; } public void setLocation(int loc) { diff --git a/MekHQ/src/mekhq/campaign/parts/ProtoMekJumpJet.java b/MekHQ/src/mekhq/campaign/parts/ProtoMekJumpJet.java index 8122c8f90f..0c2d81562b 100644 --- a/MekHQ/src/mekhq/campaign/parts/ProtoMekJumpJet.java +++ b/MekHQ/src/mekhq/campaign/parts/ProtoMekJumpJet.java @@ -60,6 +60,7 @@ public ProtoMekJumpJet clone() { public ProtoMekJumpJet(int tonnage, Campaign c) { super(tonnage, c); this.name = "ProtoMek Jump Jet"; + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/ProtoMekLegActuator.java b/MekHQ/src/mekhq/campaign/parts/ProtoMekLegActuator.java index f063f6f467..a2bf4e5299 100644 --- a/MekHQ/src/mekhq/campaign/parts/ProtoMekLegActuator.java +++ b/MekHQ/src/mekhq/campaign/parts/ProtoMekLegActuator.java @@ -54,6 +54,7 @@ public ProtoMekLegActuator clone() { public ProtoMekLegActuator(int tonnage, Campaign c) { super(tonnage, c); this.name = "ProtoMek Leg Actuator"; + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java b/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java index 29f4f13d61..6f66d4dd58 100644 --- a/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java +++ b/MekHQ/src/mekhq/campaign/parts/ProtoMekLocation.java @@ -77,6 +77,7 @@ public ProtoMekLocation(int loc, int tonnage, int structureType, boolean hasBoos this.percent = 1.0; this.forQuad = quad; this.breached = false; + this.unitTonnageMatters = true; this.name = "ProtoMek Location"; switch (loc) { case ProtoMek.LOC_HEAD: @@ -584,6 +585,11 @@ public String getDesc() { toReturn.append("") .append(isBlownOff() ? "Re-attach " : "Seal ") .append(getName()); + if(isUnitTonnageMatters()) { + toReturn.append(" (") + .append(getUnitTonnage()) + .append(" ton)"); + } if (!getCampaign().getCampaignOptions().isDestroyByMargin()) { toReturn.append(" - ") .append(ReportingUtilities.messageSurroundedBySpanWithColor( diff --git a/MekHQ/src/mekhq/campaign/parts/ProtoMekSensor.java b/MekHQ/src/mekhq/campaign/parts/ProtoMekSensor.java index 451ab8f7e3..b6c0fd5d08 100644 --- a/MekHQ/src/mekhq/campaign/parts/ProtoMekSensor.java +++ b/MekHQ/src/mekhq/campaign/parts/ProtoMekSensor.java @@ -53,6 +53,7 @@ public ProtoMekSensor clone() { public ProtoMekSensor(int tonnage, Campaign c) { super(tonnage, c); this.name = "ProtoMek Sensors"; + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/Rotor.java b/MekHQ/src/mekhq/campaign/parts/Rotor.java index b5e4a0686a..03113bcb98 100644 --- a/MekHQ/src/mekhq/campaign/parts/Rotor.java +++ b/MekHQ/src/mekhq/campaign/parts/Rotor.java @@ -46,6 +46,7 @@ public Rotor(int tonnage, Campaign c) { super(VTOL.LOC_ROTOR, tonnage, c); this.name = "Rotor"; this.damage = 0; + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/TankLocation.java b/MekHQ/src/mekhq/campaign/parts/TankLocation.java index 5ab9ce9492..125f339ef0 100644 --- a/MekHQ/src/mekhq/campaign/parts/TankLocation.java +++ b/MekHQ/src/mekhq/campaign/parts/TankLocation.java @@ -242,15 +242,22 @@ public String getDetails() { @Override public String getDetails(boolean includeRepairDetails) { + StringBuilder toReturn = new StringBuilder(); + + toReturn.append(super.getDetails(includeRepairDetails)); + if (includeRepairDetails) { if (isBreached()) { - return "Breached"; - } else { - return damage + " point(s) of damage"; + toReturn.append(", Breached"); + } else if (damage > 0) { + toReturn.append(", ") + .append(damage) + .append(damage == 1 ? " point" : " points") + .append(" of damage"); } - } else { - return super.getDetails(false); } + + return toReturn.toString(); } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/Turret.java b/MekHQ/src/mekhq/campaign/parts/Turret.java index cd3da8d6d8..b9606e5729 100644 --- a/MekHQ/src/mekhq/campaign/parts/Turret.java +++ b/MekHQ/src/mekhq/campaign/parts/Turret.java @@ -53,6 +53,7 @@ public Turret(int loc, int tonnage, Campaign c) { super(loc, tonnage, c); weight = 0; this.name = "Turret"; + this.unitTonnageMatters = true; } @Override @@ -227,15 +228,6 @@ public String getDetails() { return getDetails(true); } - @Override - public String getDetails(boolean includeRepairDetails) { - String details = weight + " tons"; - if (includeRepairDetails) { - details += ", " + damage + " point(s) of damage"; - } - return details; - } - @Override public double getTonnage() { return weight; diff --git a/MekHQ/src/mekhq/campaign/parts/equipment/JumpJet.java b/MekHQ/src/mekhq/campaign/parts/equipment/JumpJet.java index f48eee6dd6..a5a7ae970d 100644 --- a/MekHQ/src/mekhq/campaign/parts/equipment/JumpJet.java +++ b/MekHQ/src/mekhq/campaign/parts/equipment/JumpJet.java @@ -36,6 +36,7 @@ public JumpJet(int tonnage, EquipmentType et, int equipNum, boolean omniPodded, // TODO : on which it would have a different price (only tonnage is taken into // TODO : account for compatibility) super(tonnage, et, equipNum, 1.0, omniPodded, c); + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java b/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java index a1d9505b3a..6a6a89e6a0 100644 --- a/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java +++ b/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java @@ -145,6 +145,11 @@ public MissingMASC getMissingPart() { omniPodded); } + @Override + public boolean isUnitTonnageMatters() { + return !isSupercharger(); + } + @Override public String getDetails() { return getDetails(true); @@ -153,16 +158,14 @@ public String getDetails() { @Override public String getDetails(boolean includeRepairDetails) { String details = super.getDetails(includeRepairDetails); - if (null != unit) { - return details; - } if (!details.isEmpty()) { details += ", "; } if (isSupercharger()) { - return details + ", " + getEngineRating() + " rating"; + // Causes extra information but needed so omnipods show all data + details += equipTonnage + " tons, "; } - return details + ", " + getUnitTonnage() + " tons, " + getEngineRating() + " rating"; + return details + getEngineRating() + " rating"; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/equipment/MissingJumpJet.java b/MekHQ/src/mekhq/campaign/parts/equipment/MissingJumpJet.java index d83635c5c1..83e715e524 100644 --- a/MekHQ/src/mekhq/campaign/parts/equipment/MissingJumpJet.java +++ b/MekHQ/src/mekhq/campaign/parts/equipment/MissingJumpJet.java @@ -36,6 +36,7 @@ public MissingJumpJet() { public MissingJumpJet(int tonnage, EquipmentType et, int equipNum, boolean omniPodded, Campaign c) { super(tonnage, et, equipNum, c, 1, 1.0, omniPodded); + this.unitTonnageMatters = true; } @Override diff --git a/MekHQ/src/mekhq/campaign/parts/equipment/MissingMASC.java b/MekHQ/src/mekhq/campaign/parts/equipment/MissingMASC.java index e488a2dce7..ce39a09283 100644 --- a/MekHQ/src/mekhq/campaign/parts/equipment/MissingMASC.java +++ b/MekHQ/src/mekhq/campaign/parts/equipment/MissingMASC.java @@ -119,6 +119,11 @@ private boolean isSupercharger() { return type.hasSubType(MiscType.S_SUPERCHARGER); } + @Override + public boolean isUnitTonnageMatters() { + return !isSupercharger(); + } + @Override public MASC getNewPart() { MASC epart = new MASC(getUnitTonnage(), type, -1, campaign, engineRating, omniPodded); From c356a70028a0249879a4dba46e5f6cdda7b3b01d Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 11:52:15 -0700 Subject: [PATCH 040/118] Make TurretLock save and load like a Part --- MekHQ/src/mekhq/campaign/parts/TurretLock.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/TurretLock.java b/MekHQ/src/mekhq/campaign/parts/TurretLock.java index 3afd4fb2b7..9ce3b214bd 100644 --- a/MekHQ/src/mekhq/campaign/parts/TurretLock.java +++ b/MekHQ/src/mekhq/campaign/parts/TurretLock.java @@ -35,6 +35,11 @@ * @author Jay Lawson (jaylawson39 at yahoo.com) */ public class TurretLock extends Part { + public TurretLock() { + // Needed for loading from save + this(null); + } + public TurretLock(Campaign c) { super(0, c); this.name = "Turret Lock"; @@ -73,14 +78,15 @@ public boolean isSamePartType(Part part) { } @Override - protected void loadFieldsFromXmlNode(Node wn) { - // TODO Auto-generated method stub - + public void writeToXML(final PrintWriter pw, int indent) { + // Just use Part's writer + indent = writeToXMLBegin(pw, indent); + writeToXMLEnd(pw, indent); } @Override - public void writeToXML(final PrintWriter pw, int indent) { - // TODO Auto-generated method stub + protected void loadFieldsFromXmlNode(Node wn) { + // Since we're not adding any bits above Part, think this can be empty } @Override From cdb98af0cad94f0add85352c88bd0b95f425f1d0 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Fri, 18 Oct 2024 14:40:32 -0500 Subject: [PATCH 041/118] Added Duplicate Kill Checking for Multi-Crewed Units. Introduced a unique award identifier in `Kill.java` and updated `KillAwards.java` to use this to filter duplicate kills in multi-crewed units. This ensures accurate kill counts and prevents duplicate rewards. --- MekHQ/src/mekhq/campaign/Kill.java | 25 ++++-- .../personnel/autoAwards/KillAwards.java | 89 ++++++++++--------- 2 files changed, 66 insertions(+), 48 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Kill.java b/MekHQ/src/mekhq/campaign/Kill.java index 286922753f..07259f691b 100644 --- a/MekHQ/src/mekhq/campaign/Kill.java +++ b/MekHQ/src/mekhq/campaign/Kill.java @@ -21,16 +21,15 @@ */ package mekhq.campaign; -import java.io.PrintWriter; -import java.time.LocalDate; -import java.util.UUID; - -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import megamek.Version; import megamek.logging.MMLogger; import mekhq.utilities.MHQXMLUtility; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.PrintWriter; +import java.time.LocalDate; +import java.util.UUID; /** * A kill record @@ -120,6 +119,18 @@ public void setForceId(final int id) { forceId = id; } + /** + * Returns a unique identifier for the award based on the combination of various factors. + *

+ * This is used by autoAwards to identify duplicate kills across multi-crewed units. + * + * @return The string representation of the award identifier combining {@code killed}, + * {@code missionId}, {@code scenarioId}, {@code forceId}, and {@code unitType} + */ + public String getAwardIdentifier() { + return killed + missionId + scenarioId + forceId + unitType; + } + /** * @return the long corresponding to the Entity type killed, * or -1 if the kill does not have a unit type logged diff --git a/MekHQ/src/mekhq/campaign/personnel/autoAwards/KillAwards.java b/MekHQ/src/mekhq/campaign/personnel/autoAwards/KillAwards.java index 409afbd1c3..721e63e773 100644 --- a/MekHQ/src/mekhq/campaign/personnel/autoAwards/KillAwards.java +++ b/MekHQ/src/mekhq/campaign/personnel/autoAwards/KillAwards.java @@ -18,25 +18,6 @@ */ package mekhq.campaign.personnel.autoAwards; -import static mekhq.campaign.force.FormationLevel.ARMY; -import static mekhq.campaign.force.FormationLevel.BATTALION; -import static mekhq.campaign.force.FormationLevel.BRIGADE; -import static mekhq.campaign.force.FormationLevel.COMPANY; -import static mekhq.campaign.force.FormationLevel.CORPS; -import static mekhq.campaign.force.FormationLevel.DIVISION; -import static mekhq.campaign.force.FormationLevel.LANCE; -import static mekhq.campaign.force.FormationLevel.NONE; -import static mekhq.campaign.force.FormationLevel.REGIMENT; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; -import java.util.UUID; - import megamek.logging.MMLogger; import mekhq.campaign.Campaign; import mekhq.campaign.Kill; @@ -45,6 +26,10 @@ import mekhq.campaign.mission.Mission; import mekhq.campaign.personnel.Award; +import java.util.*; + +import static mekhq.campaign.force.FormationLevel.*; + public class KillAwards { private static final MMLogger logger = MMLogger.create(KillAwards.class); @@ -136,12 +121,22 @@ public static Map> KillAwardProcessor(Campaign campaign, M // origin force // (the one named after the campaign), we can cheat and just total all kills if (maximumDepth.getDepth() < awardDepth.getDepth()) { - killCount.add(killData.keySet().stream() - .mapToInt(force -> force) - .filter(force -> force != -1) // a value of -1 means no force was recorded for that - // kill - .map(force -> killData.get(force).size()) - .sum()); + Set identifiers = new HashSet<>(); + List sanitizedKills = new ArrayList<>(); + + for (Integer force : killData.keySet()) { + if (force != -1) { // a value of -1 implies no force was recorded for that kill + for (Kill kill : killData.get(force)) { + String awardIdentifier = kill.getAwardIdentifier(); + if (!identifiers.contains(awardIdentifier)) { + identifiers.add(awardIdentifier); + sanitizedKills.add(kill); + } + } + } + } + + killCount.add(sanitizedKills.size()); // if we can't cheat, we need to read the TO&E and gather a list of appropriate // kill counts. @@ -163,8 +158,7 @@ public static Map> KillAwardProcessor(Campaign campaign, M // but that's ok, in that case we just use a default value try { originForce = campaign.getPerson(person).getUnit().getForceId(); - } catch (Exception ignored) { - } + } catch (Exception ignored) {} if ((originForce != -1) && (!forceCredits.contains(originForce))) { forceCredits.add(originForce); @@ -174,7 +168,7 @@ public static Map> KillAwardProcessor(Campaign campaign, M // walking through the TO&E and gathering any associated kills for (int forceId : forceCredits) { originForce = forceId; - int kills; + List temporaryKills = new ArrayList<>(); try { // Get the current formation depth of the origin force @@ -198,11 +192,25 @@ public static Map> KillAwardProcessor(Campaign campaign, M } Force originNode = campaign.getForce(originForce); - kills = walkToeForKills(killData, originNode, new HashSet<>(forceCredits)); + temporaryKills = walkToeForKills(killData, originNode, new HashSet<>(forceCredits)); } catch (Exception e) { - kills = killData.get(forceId).size(); + temporaryKills.addAll(killData.get(forceId)); } + Set identifiers = new HashSet<>(); + List sanitizedKills = new ArrayList<>(); + + for (Kill kill : temporaryKills) { + String awardIdentifier = kill.getAwardIdentifier(); + + if (!identifiers.contains(awardIdentifier)) { + identifiers.add(awardIdentifier); + sanitizedKills.add(kill); + } + } + + + int kills = sanitizedKills.size(); if (kills >= killsNeeded) { killCount.add(kills); break; @@ -301,20 +309,19 @@ public static Map> KillAwardProcessor(Campaign campaign, M } /** - * Calculates the total number of kills from a given originNode in the killData - * map. + * Traverses the graph of Forces, starting from an origin Force, to collect associated kills from each eligible Force node in the graph. + * The traversal uses a depth-first search approach to visit each Force node. * - * @param killData the map of kill records mapped to Force ID - * @param originNode the Force node to start the traversal from - * @param forceCredits the set of Force IDs eligible for kills - * @return the total number of kills for the originNode + * @param killData the map containing kill records wherein the key is the Force ID and the value is a list of associated Kill objects. + * @param originNode the initial Force node from which the traversal begins. + * @param forceCredits the set containing the Force ID's that are eligible for collecting kills. + * @return a list of Kill objects that are associated with the traversed Force nodes that are also present in the 'forceCredits' set. */ - private static int walkToeForKills(Map> killData, Force originNode, Set forceCredits) { - int kills = 0; + private static List walkToeForKills(Map> killData, Force originNode, Set forceCredits) { + List kills = new ArrayList<>(); Stack stack = new Stack<>(); - // we add visited nodes to a set, so we don't run the risk of re-evaluating - // previously visited nodes + // we add visited nodes to a set, so we don't run the risk of re-evaluating previously visited nodes Set visitedForces = new HashSet<>(); stack.push(originNode); @@ -323,7 +330,7 @@ private static int walkToeForKills(Map> killData, Force orig if (!visitedForces.contains(currentNode.getId())) { if (forceCredits.contains(currentNode.getId())) { - kills += killData.get(currentNode.getId()).size(); + kills.addAll(killData.get(currentNode.getId())); } for (Force subForce : currentNode.getSubForces()) { From a4261d7bfb39561fd5fae773e517911e74ebed30 Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 12:50:09 -0700 Subject: [PATCH 042/118] Missing part procurment details update --- .../src/mekhq/campaign/parts/MissingPart.java | 33 +++++++++++++++---- .../mekhq/campaign/parts/PartInventory.java | 14 +++++++- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/MissingPart.java b/MekHQ/src/mekhq/campaign/parts/MissingPart.java index dbd1e504e0..a0ad33a101 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingPart.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingPart.java @@ -84,7 +84,7 @@ public boolean isSamePartType(Part part) { @Override public String getDesc() { StringBuilder toReturn = new StringBuilder(); - toReturn.append("Replace ") + toReturn.append("Replace ") .append(getName()); if(isUnitTonnageMatters()) { toReturn.append(" (") @@ -119,7 +119,9 @@ public String getDesc() { @Override public String succeed() { fix(); - return " replaced."; + return ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor(), + " replaced."); } @Override @@ -178,7 +180,7 @@ public Part findReplacement(boolean refit) { return part; } else if (bestPart.needsFixing() && !part.needsFixing()) { return part; - } else if (bestPart.getQuality() < part.getQuality()) { + } else if (bestPart.getQuality().toNumeric() < part.getQuality().toNumeric()) { return part; } } @@ -206,13 +208,30 @@ public String getDetails() { @Override public String getDetails(boolean includeRepairDetails) { + PartInventory inventories = campaign.getPartInventory(getNewPart()); + StringBuilder toReturn = new StringBuilder(); + if (isReplacementAvailable()) { - return "Replacement part available"; + toReturn.append(inventories.getSupply()) + .append(" in stock"); } else { - PartInventory inventories = campaign.getPartInventory(getNewPart()); - return "No replacement (" + inventories.getTransitOrderedDetails() + ")"; + toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "None in stock")); } - } + + String incoming = inventories.getTransitOrderedDetails(); + if (!incoming.isEmpty()) { + StringBuilder incomingSB = new StringBuilder(); + + incomingSB.append(" (") + .append(ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorWarningHexColor(), incoming)) + .append(")"); + + toReturn.append(incomingSB.toString()); + } + return toReturn.toString(); + } @Override public boolean needsFixing() { diff --git a/MekHQ/src/mekhq/campaign/parts/PartInventory.java b/MekHQ/src/mekhq/campaign/parts/PartInventory.java index 440dac511d..12f9d51c5e 100644 --- a/MekHQ/src/mekhq/campaign/parts/PartInventory.java +++ b/MekHQ/src/mekhq/campaign/parts/PartInventory.java @@ -140,6 +140,18 @@ public void setCountModifier(String countModifier) { * @see #orderedAsString() */ public String getTransitOrderedDetails() { - return transitAsString() + " in transit, " + orderedAsString() + " on order"; + StringBuilder toReturn = new StringBuilder(); + if(transit > 0) { + toReturn.append(transitAsString()) + .append(" in transit"); + } + if(ordered > 0) { + if (transit > 0) { + toReturn.append(", "); + } + toReturn.append(orderedAsString()) + .append(" on order"); + } + return toReturn.toString(); } } From 2bce8f8e8415dd688b707a2d184dd50d8c9213ce Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 12:55:43 -0700 Subject: [PATCH 043/118] Update a bunch of deprecated campaign.getPart()s --- MekHQ/src/mekhq/campaign/parts/PodSpace.java | 26 ++++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/PodSpace.java b/MekHQ/src/mekhq/campaign/parts/PodSpace.java index c0fc645a43..5e6d4d5d0b 100644 --- a/MekHQ/src/mekhq/campaign/parts/PodSpace.java +++ b/MekHQ/src/mekhq/campaign/parts/PodSpace.java @@ -78,7 +78,7 @@ public int getBaseTime() { } public List getPartList() { - return childPartIds.stream().map(id -> campaign.getPart(id)) + return childPartIds.stream().map(id -> campaign.getWarehouse().getPart(id)) .filter(Objects::nonNull).collect(Collectors.toList()); } @@ -102,7 +102,7 @@ public void remove(boolean salvage) { shorthandedMod = 0; //Iterate through all pod-mounted equipment in space and remove them. for (int pid : childPartIds) { - final Part part = campaign.getPart(pid); + final Part part = campaign.getWarehouse().getPart(pid); if (part != null) { part.remove(salvage); MekHQ.triggerEvent(new PartChangedEvent(part)); @@ -115,7 +115,7 @@ public void remove(boolean salvage) { public void fix() { shorthandedMod = 0; for (int pid : childPartIds) { - final Part part = campaign.getPart(pid); + final Part part = campaign.getWarehouse().getPart(pid); if (part != null && !(part instanceof MissingPart) && !(part instanceof AmmoBin) && part.needsFixing() @@ -126,7 +126,7 @@ public void fix() { } updateConditionFromEntity(false); for (int pid : childPartIds) { - final Part part = campaign.getPart(pid); + final Part part = campaign.getWarehouse().getPart(pid); if (part instanceof MissingPart) { part.fix(); MekHQ.triggerEvent(new PartChangedEvent(part)); @@ -156,7 +156,7 @@ public MissingPart getMissingPart() { } if (repairInPlace) { for (int id : childPartIds) { - final Part p = unit.getCampaign().getPart(id); + final Part p = unit.getCampaign().getWarehouse().getPart(id); if (p instanceof MissingPart) { return null; } @@ -164,7 +164,7 @@ public MissingPart getMissingPart() { return unit.getEntity().getLocationName(location) + " is not missing any pod-mounted equipment."; } else { for (int id : childPartIds) { - final Part p = unit.getCampaign().getPart(id); + final Part p = unit.getCampaign().getWarehouse().getPart(id); if (p == null || !p.needsFixing()) { continue; } @@ -188,7 +188,7 @@ public MissingPart getMissingPart() { @Override public boolean needsFixing() { return childPartIds.stream() - .map(id -> campaign.getPart(id)).filter(Objects::nonNull) + .map(id -> campaign.getWarehouse().getPart(id)).filter(Objects::nonNull) .anyMatch(p -> !(p instanceof AmmoBin) && p.needsFixing()); } @@ -247,7 +247,7 @@ public String fail(int rating) { shorthandedMod = 0; boolean replacing = false; for (int id : childPartIds) { - final Part part = campaign.getPart(id); + final Part part = campaign.getWarehouse().getPart(id); if (part != null && (isSalvaging() || (!(part instanceof AmmoBin) && part.needsFixing()))) { part.fail(rating); @@ -280,7 +280,7 @@ public String getPartName() { public int getSkillMin() { int minSkill = SkillType.EXP_GREEN; for (int id : childPartIds) { - final Part part = campaign.getPart(id); + final Part part = campaign.getWarehouse().getPart(id); if (part != null) { if ((isSalvaging() && !(part instanceof MissingPart)) || (!isSalvaging() && (part instanceof MissingPart) @@ -402,7 +402,7 @@ public String getDetails(boolean includeRepairDetails) { int inTransit = 0; int onOrder = 0; for (int id : childPartIds) { - Part part = campaign.getPart(id); + Part part = campaign.getWarehouse().getPart(id); if (part != null) { if (!isSalvaging() && !(part instanceof AmmoBin) && part.needsFixing()) { allParts++; @@ -461,7 +461,7 @@ public void setRepairInPlace(boolean repairInPlace) { public boolean hasSalvageableParts() { for (int id : childPartIds) { - final Part p = campaign.getPart(id); + final Part p = campaign.getWarehouse().getPart(id); if (p != null && p.isSalvaging()) { return true; } @@ -471,13 +471,13 @@ public boolean hasSalvageableParts() { @Override public void reservePart() { - childPartIds.stream().map(id -> campaign.getPart(id)) + childPartIds.stream().map(id -> campaign.getWarehouse().getPart(id)) .filter(Objects::nonNull).forEach(Part::reservePart); } @Override public void cancelReservation() { - childPartIds.stream().map(id -> campaign.getPart(id)) + childPartIds.stream().map(id -> campaign.getWarehouse().getPart(id)) .filter(Objects::nonNull).forEach(Part::cancelReservation); } From c3ce90294e519fa25cb58883b3bfbbdbbc07dd92 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Fri, 18 Oct 2024 15:06:25 -0500 Subject: [PATCH 044/118] Fix isMilitary and isPrepSchool flags; correct typo. Changed isMilitary and isPrepSchool flags to false for Adult Apprenticeship in Unit Education.xml. Corrected spelling of "MechWarrior" in Prestigious Academies.xml. These adjustments ensure data accuracy and consistency across the files. --- MekHQ/data/universe/academies/Prestigious Academies.xml | 8 ++++---- MekHQ/data/universe/academies/Unit Education.xml | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/MekHQ/data/universe/academies/Prestigious Academies.xml b/MekHQ/data/universe/academies/Prestigious Academies.xml index d133f3ad34..bdce2b6f5b 100644 --- a/MekHQ/data/universe/academies/Prestigious Academies.xml +++ b/MekHQ/data/universe/academies/Prestigious Academies.xml @@ -438,10 +438,10 @@ 0 - Allison MekWarrior Institute + Allison MechWarrior Institute Military Academy true - The Allison MekWarrior Institute stands as a premier training facility for MekWarriors, adjacent to the Lloyd Marik-Stanley Aerospace School. Producing 375 MekWarrior graduates annually, the institute fosters a spirited rivalry with neighboring aerospace cadets, characterized by athletic contests and pranks that occasionally escalate into conflicts. Admission is highly competitive, influenced by political considerations, with a portion of spots reserved for families of Dispossessed Mechwarriors to uphold loyalty. + The Allison MechWarrior Institute stands as a premier training facility for MekWarriors, adjacent to the Lloyd Marik-Stanley Aerospace School. Producing 375 MekWarrior graduates annually, the institute fosters a spirited rivalry with neighboring aerospace cadets, characterized by athletic contests and pranks that occasionally escalate into conflicts. Admission is highly competitive, influenced by political considerations, with a portion of spots reserved for families of Dispossessed Mechwarriors to uphold loyalty. New Olympia 2465 5250 @@ -456,10 +456,10 @@ 0 - Allison MekWarrior Institute (Officer) + Allison MechWarrior Institute (Officer) Officer Academy true - The Allison MekWarrior Institute stands as a premier training facility for MekWarriors, adjacent to the Lloyd Marik-Stanley Aerospace School. Producing 375 MekWarrior graduates annually, the institute fosters a spirited rivalry with neighboring aerospace cadets, characterized by athletic contests and pranks that occasionally escalate into conflicts. Admission is highly competitive, influenced by political considerations, with a portion of spots reserved for families of Dispossessed Mechwarriors to uphold loyalty. + The Allison MechWarrior Institute stands as a premier training facility for MekWarriors, adjacent to the Lloyd Marik-Stanley Aerospace School. Producing 375 MekWarrior graduates annually, the institute fosters a spirited rivalry with neighboring aerospace cadets, characterized by athletic contests and pranks that occasionally escalate into conflicts. Admission is highly competitive, influenced by political considerations, with a portion of spots reserved for families of Dispossessed Mechwarriors to uphold loyalty. New Olympia 2465 13125 diff --git a/MekHQ/data/universe/academies/Unit Education.xml b/MekHQ/data/universe/academies/Unit Education.xml index 6921c60a01..1ba1337056 100644 --- a/MekHQ/data/universe/academies/Unit Education.xml +++ b/MekHQ/data/universe/academies/Unit Education.xml @@ -89,8 +89,7 @@ Adult Apprenticeship - true - true + false An adult apprenticeship is a hands-on training program where older recruits are integrated into a military or mercenary unit. The apprenticeship focuses heavily on learning in the field, with recruits shadowing seasoned veterans. true Terra From d5eac8e7c41837ce925973920928eb33cefc5d5e Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 13:15:25 -0700 Subject: [PATCH 045/118] MissingPart - Update to use ReportingUtilities --- .../src/mekhq/campaign/parts/MissingPart.java | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/MissingPart.java b/MekHQ/src/mekhq/campaign/parts/MissingPart.java index a0ad33a101..7e484f7f3a 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingPart.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingPart.java @@ -224,11 +224,11 @@ public String getDetails(boolean includeRepairDetails) { StringBuilder incomingSB = new StringBuilder(); incomingSB.append(" (") - .append(ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorWarningHexColor(), incoming)) + .append(incoming) .append(")"); - toReturn.append(incomingSB.toString()); + toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorWarningHexColor(), incomingSB.toString())); } return toReturn.toString(); } @@ -264,9 +264,13 @@ public String fail(int rating) { part.decrementQuantity(); skillMin = SkillType.EXP_GREEN; } - return " failed and part destroyed."; + return ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), + " failed and part destroyed") + "."; } else { - return " failed."; + return ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), + " failed") + "."; } } @@ -345,14 +349,24 @@ public Part getAcquisitionPart() { @Override public String find(int transitDays) { + // TODO: Move me to live with procurment functions? + // Which shopping method is this used for? Part newPart = getNewPart(); newPart.setBrandNew(true); newPart.setDaysToArrival(transitDays); + StringBuilder toReturn = new StringBuilder(); if (campaign.getQuartermaster().buyPart(newPart, transitDays)) { - return " part found. It will be delivered in " + transitDays + " days."; + toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor(), " part found")) + .append(". It will be delivered in ") + .append(transitDays) + .append(" days."); } else { - return " You cannot afford this part. Transaction cancelled."; + toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), + " You cannot afford this part. Transaction cancelled")); } + return toReturn.toString(); } @Override @@ -364,7 +378,9 @@ public Object getNewEquipment() { @Override public String failToFind() { - return " part not found."; + // TODO: Move me to live with procurment functions? + return ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), " part not found") + "."; } @Override From 19447fa1c43a22bd6506df564b8f10a1092bd844 Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 13:23:06 -0700 Subject: [PATCH 046/118] Part - Update to use ReportingUtiltites --- MekHQ/src/mekhq/campaign/parts/Part.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/Part.java b/MekHQ/src/mekhq/campaign/parts/Part.java index 1446f27dcd..f12409a360 100644 --- a/MekHQ/src/mekhq/campaign/parts/Part.java +++ b/MekHQ/src/mekhq/campaign/parts/Part.java @@ -1031,18 +1031,20 @@ public String fail(int rating) { skillMin = ++rating; timeSpent = 0; shorthandedMod = 0; - return " failed."; + return ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "' failed") + "."; } @Override public String succeed() { if (isSalvaging()) { remove(true); - return " salvaged."; + return ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor(), " salvaged") + "."; } else { fix(); - return " fixed."; + return ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor(), " fixed") + "."; } } From 58e6fa066a8a38b842dde31fc2e0d68d27baeebe Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 13:46:48 -0700 Subject: [PATCH 047/118] Part.getOrderTransitSTringForDetails duplicates function in PartInventory so get rid of it --- MekHQ/src/mekhq/campaign/parts/Armor.java | 8 ++++---- MekHQ/src/mekhq/campaign/parts/Part.java | 19 ------------------- .../campaign/parts/equipment/AmmoBin.java | 10 +++++----- 3 files changed, 9 insertions(+), 28 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/Armor.java b/MekHQ/src/mekhq/campaign/parts/Armor.java index 6cc9c10abe..b69a916089 100644 --- a/MekHQ/src/mekhq/campaign/parts/Armor.java +++ b/MekHQ/src/mekhq/campaign/parts/Armor.java @@ -170,17 +170,17 @@ public String getDetails(boolean includeRepairDetails) { int amountAvailable = getAmountAvailable(); PartInventory inventories = campaign.getPartInventory(getNewPart()); - String orderTransitString = getOrderTransitStringForDetails(inventories); + String orderTransitString = inventories.getTransitOrderedDetails(); if (amountAvailable == 0) { availability = "
No armor " + orderTransitString + ""; + + "'>No armor (" + orderTransitString + ")"; } else if (amountAvailable < amountNeeded) { availability = "
Only " + amountAvailable + " available " + orderTransitString + ""; + + "'>Only " + amountAvailable + " available (" + orderTransitString + ")"; } else { availability = "
" - + amountAvailable + " available " + orderTransitString + ""; + + amountAvailable + " available (" + orderTransitString + ")"; } return unit.getEntity().getLocationName(location) + rearMount + ", " + amountNeeded + " points" diff --git a/MekHQ/src/mekhq/campaign/parts/Part.java b/MekHQ/src/mekhq/campaign/parts/Part.java index f12409a360..e3bc345598 100644 --- a/MekHQ/src/mekhq/campaign/parts/Part.java +++ b/MekHQ/src/mekhq/campaign/parts/Part.java @@ -1096,25 +1096,6 @@ public String getDetails(boolean includeRepairDetails) { return sj.toString(); } - /** - * Converts the array of strings normally returned by a call to - * campaign.getInventory() - * to a string that reads like "(x in transit, y on order)" - * - * @param inventories The inventory array, see campaign.getInventory() for - * details. - * @return Human readable string. - */ - public String getOrderTransitStringForDetails(PartInventory inventories) { - String inTransitString = (inventories.getTransit() == 0) ? "" : inventories.transitAsString() + " in transit"; - String onOrderString = (inventories.getOrdered() == 0) ? "" : inventories.orderedAsString() + " on order"; - String transitOrderSeparator = !inTransitString.isBlank() && !onOrderString.isBlank() ? ", " : ""; - - return (!inTransitString.isBlank() || !onOrderString.isBlank()) - ? String.format("(%s%s%s)", inTransitString, transitOrderSeparator, onOrderString) - : ""; - } - @Override public boolean isSalvaging() { if (null != unit) { diff --git a/MekHQ/src/mekhq/campaign/parts/equipment/AmmoBin.java b/MekHQ/src/mekhq/campaign/parts/equipment/AmmoBin.java index d41dac0078..8ad4734870 100644 --- a/MekHQ/src/mekhq/campaign/parts/equipment/AmmoBin.java +++ b/MekHQ/src/mekhq/campaign/parts/equipment/AmmoBin.java @@ -517,17 +517,17 @@ public String getDetails(boolean includeRepairDetails) { int shotsAvailable = getAmountAvailable(); PartInventory inventories = campaign.getPartInventory(getNewPart()); - String orderTransitString = getOrderTransitStringForDetails(inventories); + String orderTransitString = inventories.getTransitOrderedDetails(); if (shotsAvailable == 0) { - availability = "
No ammo " - + orderTransitString + ""; + availability = "
No ammo (" + + orderTransitString + ")"; } else if (shotsAvailable < getShotsNeeded()) { availability = "
Only " - + shotsAvailable + " available" + orderTransitString + ""; + + shotsAvailable + " available (" + orderTransitString + ")"; } else { availability = "
" - + shotsAvailable + " available " + orderTransitString + ""; + + shotsAvailable + " available (" + orderTransitString + ")"; } return getType().getDesc() + ", " + getShotsNeeded() + " shots needed" + availability; From a041f06f614c15f049b2cc7ff936f09ac1932bc1 Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 14:40:42 -0700 Subject: [PATCH 048/118] AmmoBin - Update GetDesc and GetDetails --- .../campaign/parts/equipment/AmmoBin.java | 69 ++++++++++++------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/equipment/AmmoBin.java b/MekHQ/src/mekhq/campaign/parts/equipment/AmmoBin.java index 8ad4734870..46b114a018 100644 --- a/MekHQ/src/mekhq/campaign/parts/equipment/AmmoBin.java +++ b/MekHQ/src/mekhq/campaign/parts/equipment/AmmoBin.java @@ -43,6 +43,7 @@ import mekhq.campaign.unit.Unit; import mekhq.campaign.work.IAcquisitionWork; import mekhq.utilities.MHQXMLUtility; +import mekhq.utilities.ReportingUtilities; /** * @author Jay Lawson (jaylawson39 at yahoo.com) @@ -488,18 +489,18 @@ public String getDesc() { if (isSalvaging()) { return super.getDesc(); } - String toReturn = "
"; - toReturn += getDetails() + "
"; - toReturn += "" + getTimeLeft() + " minutes" + scheduled; - toReturn += ""; - return toReturn; + StringBuilder toReturn = new StringBuilder(); + toReturn.append("") + .append("Reload ") + .append(getName()) + .append("
") + .append(getDetails()) + .append("
") + .append(getTimeLeft()) + .append(" minutes") + .append(null != getTech() ? " (scheduled) " : "") + .append(""); + return toReturn.toString(); } @Override @@ -513,24 +514,44 @@ public String getDetails(boolean includeRepairDetails) { return super.getDetails(includeRepairDetails); } if (null != unit) { - String availability; int shotsAvailable = getAmountAvailable(); PartInventory inventories = campaign.getPartInventory(getNewPart()); - - String orderTransitString = inventories.getTransitOrderedDetails(); - - if (shotsAvailable == 0) { - availability = "
No ammo (" - + orderTransitString + ")"; + + StringBuilder toReturn = new StringBuilder(); + toReturn.append(getType().getDesc()) + .append(", ") + .append(getShotsNeeded()) + .append(" shots needed") + .append("
"); + + if(shotsAvailable == 0) { + toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "None in stock")); } else if (shotsAvailable < getShotsNeeded()) { - availability = "
Only " - + shotsAvailable + " available (" + orderTransitString + ")"; + toReturn.append(ReportingUtilities.spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor())) + .append("Only ") + .append(shotsAvailable) + .append(" in stock") + .append(ReportingUtilities.CLOSING_SPAN_TAG); } else { - availability = "
" - + shotsAvailable + " available (" + orderTransitString + ")"; + toReturn.append(ReportingUtilities.spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor())) + .append(shotsAvailable) + .append(" in stock") + .append(ReportingUtilities.CLOSING_SPAN_TAG); } - return getType().getDesc() + ", " + getShotsNeeded() + " shots needed" + availability; + String orderTransitString = inventories.getTransitOrderedDetails(); + if (!orderTransitString.isEmpty()) { + toReturn.append(ReportingUtilities.spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorWarningHexColor())) + .append(" (") + .append(orderTransitString) + .append(")") + .append(ReportingUtilities.CLOSING_SPAN_TAG); + } + return toReturn.toString(); } else { return ""; } From 68bcaaaccf686e63ef947d1b51f51ca8fe74b6ba Mon Sep 17 00:00:00 2001 From: Setsul <19393727+Setsul@users.noreply.github.com> Date: Sat, 19 Oct 2024 00:04:58 +0200 Subject: [PATCH 049/118] Bug fix and improvements for calculateContractDifficulty() Properly uses average BV across all Meks instead of random ones. Corrected ally ratio for House command, best guess magic number for Liaison for now. --- .../mekhq/campaign/mission/AtBContract.java | 84 ++++++++++++------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 5351c7dc5a..81eb316b13 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -43,6 +43,7 @@ import mekhq.campaign.mission.atb.AtBScenarioFactory; import mekhq.campaign.mission.enums.AtBContractType; import mekhq.campaign.mission.enums.AtBMoraleLevel; +import mekhq.campaign.mission.enums.ContractCommandRights; import mekhq.campaign.personnel.Bloodname; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.backgrounds.BackgroundsController; @@ -1767,7 +1768,7 @@ public int calculateContractDifficulty(Campaign campaign) { // Estimate the power of the enemy forces SkillLevel opposingSkill = modifySkillLevelBasedOnFaction(enemyCode, enemySkill); double enemySkillMultiplier = getSkillMultiplier(opposingSkill); - int enemyPower = getAverageBattleValue(campaign, enemyCode, enemyQuality); + double enemyPower = estimateMekStrength(campaign, enemyCode, enemyQuality); // If we cannot calculate enemy power, abort. if (enemyPower == 0) { @@ -1777,26 +1778,30 @@ public int calculateContractDifficulty(Campaign campaign) { enemyPower = (int) round(enemyPower * enemySkillMultiplier); // Estimate player power - int playerPower = estimatePlayerPower(campaign); + double playerPower = estimatePlayerPower(campaign); // Estimate the power of allied forces - int allyPower = 0; - if (!getCommandRights().isIndependent()) { + // TODO pull these directly from Force Generation instead of using magic numbers + // TODO estimate the LIAISON ratio by going through each combat lance and + // getting the actual average (G)BV for an allied heavy/assault mek. + double allyRatio = switch (getCommandRights()) { + case INDEPENDENT -> 0; // no allies + case LIAISON -> 0.4; // single allied heavy/assault mek, pure guess for now + case HOUSE -> 1; // allies with same (G)BV budget + case INTEGRATED -> 2; // allies with twice the player's (G)BV budget + default -> 0; + }; + if (allyRatio > 0) { SkillLevel alliedSkill = modifySkillLevelBasedOnFaction(employerCode, allySkill); double allySkillMultiplier = getSkillMultiplier(alliedSkill); - allyPower = getAverageBattleValue(campaign, employerCode, allyQuality); - + double allyPower = estimateMekStrength(campaign, employerCode, allyQuality); + allyPower = allyPower * allySkillMultiplier; // If we cannot calculate ally's power, use player power as a fallback. if (allyPower == 0) { allyPower = playerPower; } - - allyPower = (int) round(allyPower * allySkillMultiplier); - } - - if (allyPower > 0) { - playerPower += allyPower; - enemyPower *= 2; + playerPower += allyRatio * allyPower; + enemyPower += allyRatio * enemyPower; } // Calculate difficulty based on the percentage difference between the two forces. @@ -1830,10 +1835,11 @@ private static SkillLevel modifySkillLevelBasedOnFaction(String factionCode, Ski * Estimates the power of the player in a campaign based on the battle values of their units. * * @param campaign the object containing the forces and units of the player - * @return the estimated player power in the campaign + * @return average battle value per player unit OR total BV2 divided by total GBV */ - private static int estimatePlayerPower(Campaign campaign) { + private static double estimatePlayerPower(Campaign campaign) { int playerPower = 0; + int playerGBV = 0; int playerUnitCount = 0; for (Force force : campaign.getAllForces()) { if (!force.isCombatForce()) { @@ -1841,13 +1847,18 @@ private static int estimatePlayerPower(Campaign campaign) { } for (UUID unitID : force.getUnits()) { - Unit unit = campaign.getUnit(unitID); - playerPower += unit.getEntity().calculateBattleValue(); + Entity entity = campaign.getUnit(unitID).getEntity(); + playerPower += entity.calculateBattleValue(); + playerGBV += entity.getGenericBattleValue(); playerUnitCount ++; } } - return round((float) playerPower / playerUnitCount); + if (campaign.getCampaignOptions().isUseGenericBattleValue()) { + return ((double) playerPower) / playerGBV; + } else { + return ((double) playerPower) / playerUnitCount; + } } /** @@ -1869,15 +1880,17 @@ private static double getSkillMultiplier(SkillLevel skillLevel) { }; } /** - * Calculates the average battle value for Mek units of a specific faction and quality. + * Estimates the relative strength for Mek units of a specific faction and quality. + * Excludes salvage. * - * @param campaign the campaign to calculate the average battle value for - * @param factionCode the code of the faction to calculate the average battle value for - * @param quality the quality of the units to calculate the average battle value for - * @return the average battle value for units of the specified faction and quality + * @param campaign the campaign to estimate the average Mek strength for + * @param factionCode the code of the faction to estimate the average Mek strength for + * @param quality the quality of the Meks to calculate the average strength for + * @return the average battle value OR total BV2 divided by total GBV + * for Meks of the specified faction and quality OR 0 on error */ - private static int getAverageBattleValue(Campaign campaign, String factionCode, int quality) { - final int ERROR = 0; + private static double estimateMekStrength(Campaign campaign, String factionCode, int quality) { + final double ERROR = 0; RATGenerator ratGenerator = Factions.getInstance().getRATGenerator(); FactionRecord faction = ratGenerator.getFaction(factionCode); @@ -1908,17 +1921,30 @@ private static int getAverageBattleValue(Campaign campaign, String factionCode, int entries = unitTable.getNumEntries(); int totalBattleValue = 0; + int totalGBV = 0; int rollingCount = 0; for (int i = 0; i < entries; i++) { - int weight = unitTable.getEntryWeight(i); - int battleValue = unitTable.generateUnit().getBV(); - + int battleValue = unitTable.getBV(i); // 0 for salvage + if (0 == battleValue) { + // Removing this check will break things, see the other comments. + continue; + } + // TODO implement getGBV(int index) in UnitTable to simplify this? + // getMekSummary(int index) is NULL for salvage. + int genericBattleValue = unitTable.getMekSummary(i).loadEntity().getGenericBattleValue(); + int weight = unitTable.getEntryWeight(i); // NOT 0 for salvage + totalBattleValue += battleValue * weight; + totalGBV += genericBattleValue * weight; rollingCount += weight; } - return totalBattleValue / rollingCount; + if (campaign.getCampaignOptions().isUseGenericBattleValue()) { + return ((double) totalBattleValue) / totalGBV; + } else { + return ((double) totalBattleValue) / rollingCount; + } } /** From 920363d866039b77421fe597251aaa9067a050f0 Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 15:40:51 -0700 Subject: [PATCH 050/118] Armor - Update getDetails --- MekHQ/src/mekhq/campaign/parts/Armor.java | 67 +++++++++++++++-------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/Armor.java b/MekHQ/src/mekhq/campaign/parts/Armor.java index b69a916089..e395a42304 100644 --- a/MekHQ/src/mekhq/campaign/parts/Armor.java +++ b/MekHQ/src/mekhq/campaign/parts/Armor.java @@ -160,35 +160,58 @@ public String getDetails() { @Override public String getDetails(boolean includeRepairDetails) { + StringBuilder toReturn = new StringBuilder(); if (null != unit) { - String rearMount = ""; - if (rear) { - rearMount = " (R)"; - } - if (!isSalvaging()) { - String availability; - int amountAvailable = getAmountAvailable(); - PartInventory inventories = campaign.getPartInventory(getNewPart()); - - String orderTransitString = inventories.getTransitOrderedDetails(); + if (isSalvaging()) { + toReturn.append(unit.getEntity().getLocationName(location)) + .append(rear ? " (Rear)" : "") + .append(", ") + .append(amount) + .append(amount == 1 ? " point" : " points"); + } else { + toReturn.append(unit.getEntity().getLocationName(location)) + .append(rear ? " (Rear)" : "") + .append(", ") + .append(amountNeeded) + .append(amountNeeded == 1 ? " point" : " points") + .append("
"); - if (amountAvailable == 0) { - availability = "
No armor (" + orderTransitString + ")"; + int amountAvailable = getAmountAvailable(); + if(amountAvailable == 0) { + toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "None in stock")); } else if (amountAvailable < amountNeeded) { - availability = "
Only " + amountAvailable + " available (" + orderTransitString + ")"; + toReturn.append(ReportingUtilities.spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor())) + .append("Only ") + .append(amountAvailable) + .append(" in stock") + .append(ReportingUtilities.CLOSING_SPAN_TAG); } else { - availability = "
" - + amountAvailable + " available (" + orderTransitString + ")"; + toReturn.append(ReportingUtilities.spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor())) + .append(amountAvailable) + .append(" in stock") + .append(ReportingUtilities.CLOSING_SPAN_TAG); + } + + PartInventory inventories = campaign.getPartInventory(getNewPart()); + String orderTransitString = inventories.getTransitOrderedDetails(); + if (!orderTransitString.isEmpty()) { + toReturn.append(ReportingUtilities.spanOpeningWithCustomColor( + MekHQ.getMHQOptions().getFontColorWarningHexColor())) + .append(" (") + .append(orderTransitString) + .append(")") + .append(ReportingUtilities.CLOSING_SPAN_TAG); } - - return unit.getEntity().getLocationName(location) + rearMount + ", " + amountNeeded + " points" - + availability; } - return unit.getEntity().getLocationName(location) + rearMount + ", " + amount + " points"; + + } else { + toReturn.append(amount) + .append(" points"); } - return amount + " points"; + return toReturn.toString(); } public int getType() { From f42f4fde255a61bd03cd7b11a63bdb1fb9551e74 Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 16:52:36 -0700 Subject: [PATCH 051/118] Fix to show actual amount of armor on order in repair info --- MekHQ/src/mekhq/campaign/Campaign.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index ac638a50d4..bb449070e3 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -7689,7 +7689,7 @@ public PartInventory getPartInventory(Part part) { IAcquisitionWork onOrder = getShoppingList().getShoppingItem(part); if (null != onOrder) { if (onOrder instanceof Armor) { // ProtoMek Armor and BaArmor are derived from Armor - nOrdered += ((Armor) onOrder).getAmount(); + nOrdered += ((Armor) onOrder).getAmount() * ((Armor) onOrder).getQuantity(); } else if (onOrder instanceof AmmoStorage) { nOrdered += ((AmmoStorage) onOrder).getShots(); } else { From 470183a7278a3552685ddbdc64e3ec1eb659b5df Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 16:57:44 -0700 Subject: [PATCH 052/118] PartInventory - Fix doc comment --- MekHQ/src/mekhq/campaign/parts/PartInventory.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/PartInventory.java b/MekHQ/src/mekhq/campaign/parts/PartInventory.java index 12f9d51c5e..7bd966b91e 100644 --- a/MekHQ/src/mekhq/campaign/parts/PartInventory.java +++ b/MekHQ/src/mekhq/campaign/parts/PartInventory.java @@ -133,9 +133,8 @@ public void setCountModifier(String countModifier) { /** * Gets the transit and ordered counts formatted as a String. * - * @return A String like, "XXX in transit, YYY on order", - * describing - * the transit and ordered counts. + * @return A String like, "X in transit, Y on order", + * describing the transit and ordered counts. * @see #transitAsString() * @see #orderedAsString() */ From 666a4c47b3ca5c9df7f4c6b3bb0cd7b402d183d9 Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 18:03:39 -0700 Subject: [PATCH 053/118] PodSpace - more cleanup --- MekHQ/src/mekhq/campaign/parts/PodSpace.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/PodSpace.java b/MekHQ/src/mekhq/campaign/parts/PodSpace.java index 5e6d4d5d0b..7515048bff 100644 --- a/MekHQ/src/mekhq/campaign/parts/PodSpace.java +++ b/MekHQ/src/mekhq/campaign/parts/PodSpace.java @@ -234,10 +234,12 @@ public TargetRoll getAllMods(Person tech) { public String succeed() { if (isSalvaging()) { remove(true); - return " removed."; + return ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor(), " removed") + "."; } else { fix(); - return " fixed."; + return ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorPositiveHexColor(), " fixed") + "."; } } @@ -255,9 +257,12 @@ public String fail(int rating) { } } if (rating >= SkillType.EXP_ELITE && replacing) { - return " failed and part(s) destroyed."; + return ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), + " failed and part(s) destroyed") + "."; } else { - return " failed."; + return ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor()," failed") + "."; } } From d3ac8723dc82a03029410d181bbde7146c02cbe8 Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 18:44:17 -0700 Subject: [PATCH 054/118] MASC - use StringBuilder --- MekHQ/src/mekhq/campaign/parts/equipment/MASC.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java b/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java index 6a6a89e6a0..0d00540128 100644 --- a/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java +++ b/MekHQ/src/mekhq/campaign/parts/equipment/MASC.java @@ -157,15 +157,19 @@ public String getDetails() { @Override public String getDetails(boolean includeRepairDetails) { - String details = super.getDetails(includeRepairDetails); + StringBuilder details = new StringBuilder(); + details.append(super.getDetails(includeRepairDetails)); if (!details.isEmpty()) { - details += ", "; + details.append(", "); } if (isSupercharger()) { // Causes extra information but needed so omnipods show all data - details += equipTonnage + " tons, "; + details.append(equipTonnage) + .append(" tons, "); } - return details + getEngineRating() + " rating"; + details.append(getEngineRating()) + .append(" rating"); + return details.toString(); } @Override From 69c60acde4a1d6e3bfbddedec98f3168f4c81652 Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 21:13:19 -0700 Subject: [PATCH 055/118] Add parentheses to details in command center --- MekHQ/src/mekhq/campaign/parts/MissingPart.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/MissingPart.java b/MekHQ/src/mekhq/campaign/parts/MissingPart.java index 7e484f7f3a..5c9350c067 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingPart.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingPart.java @@ -412,9 +412,17 @@ public String scrap() { @Override public String getAcquisitionName() { + // TODO: Unify shopping system to use these everywhere instead of only some places? + StringBuilder toReturn = new StringBuilder(); + toReturn.append(getPartName()); + String details = getNewPart().getDetails(); - details = details.replaceFirst("\\d+\\shit\\(s\\)", ""); - return getPartName() + ' ' + details; + if (!details.isEmpty()) { + toReturn.append(" (") + .append(details) + .append(')'); + } + return toReturn.toString(); } @Override From 87a33a69fae09df4c242b7e6cacf9b8d2396b858 Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 21:32:16 -0700 Subject: [PATCH 056/118] MekLocation - fixed; MekLocationTest - updated --- .../src/mekhq/campaign/parts/MekLocation.java | 38 ++++++++----------- .../mekhq/campaign/parts/MekLocationTest.java | 27 +++++++------ 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/MekLocation.java b/MekHQ/src/mekhq/campaign/parts/MekLocation.java index 210f740b02..122150a7ec 100644 --- a/MekHQ/src/mekhq/campaign/parts/MekLocation.java +++ b/MekHQ/src/mekhq/campaign/parts/MekLocation.java @@ -570,18 +570,22 @@ public String getDetails() { public String getDetails(boolean includeRepairDetails) { StringBuilder toReturn = new StringBuilder(); - toReturn.append(super.getDetails(includeRepairDetails)); + if (null != getUnit()) { + toReturn.append(Objects.requireNonNull(getUnit()).getEntity().getLocationName(loc)) + .append(", "); + } + toReturn.append(getUnitTonnage()) + .append(" tons"); + if (loc == Mek.LOC_HEAD) { StringJoiner components = new StringJoiner(", "); if (hasSensors()) { components.add("Sensors"); } - if (hasLifeSupport()) { components.add("Life Support"); } - if (components.length() > 0) { toReturn.append(" [") .append(components) @@ -589,31 +593,21 @@ public String getDetails(boolean includeRepairDetails) { } } - if (getUnit() != null) { - return getDetailsOnUnit(includeRepairDetails); - } - - if (includeRepairDetails && getPercent() < 1.0) { - toReturn.append(" (") - .append(Math.round(100 * getPercent())) - .append("%)"); - } - - return toReturn.toString(); - } - - private String getDetailsOnUnit(boolean includeRepairDetails) { - String toReturn = Objects.requireNonNull(getUnit()).getEntity().getLocationName(loc); if (includeRepairDetails) { if (isBlownOff()) { - toReturn += " (Blown Off)"; + toReturn.append(" (Blown Off)"); } else if (isBreached()) { - toReturn += " (Breached)"; + toReturn.append(" (Breached)"); } else if (onBadHipOrShoulder()) { - toReturn += " (Bad Hip/Shoulder)"; + toReturn.append(" (Bad Hip/Shoulder)"); + } else if (getPercent() < 1.0) { + toReturn.append(" (") + .append(Math.round(100 * getPercent())) + .append("%)"); } } - return toReturn; + + return toReturn.toString(); } @Override diff --git a/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java b/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java index a3e07fe36f..380ebed1ea 100644 --- a/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java +++ b/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java @@ -2022,7 +2022,6 @@ void getDetailsSpareTest() { assertNotNull(mekLocation.getDetails()); assertTrue(mekLocation.getDetails().startsWith("25 tons")); - assertTrue(mekLocation.getDetails().contains("(100%)")); assertNotNull(mekLocation.getDetails(false)); assertEquals("25 tons", mekLocation.getDetails(false)); @@ -2037,7 +2036,7 @@ void getDetailsSpareTest() { assertNotNull(mekLocation.getDetails()); assertTrue(mekLocation.getDetails().startsWith("25 tons")); - assertTrue(mekLocation.getDetails().contains("(100%)")); + //assertTrue(mekLocation.getDetails().contains("(100%)")); assertNotNull(mekLocation.getDetails(false)); assertEquals("25 tons", mekLocation.getDetails(false)); @@ -2045,7 +2044,7 @@ void getDetailsSpareTest() { assertNotNull(mekLocation.getDetails()); assertTrue(mekLocation.getDetails().startsWith("25 tons")); - assertTrue(mekLocation.getDetails().contains("(100%)")); + //assertTrue(mekLocation.getDetails().contains("(100%)")); assertTrue(mekLocation.getDetails().contains("[Sensors]")); assertNotNull(mekLocation.getDetails(false)); assertEquals("25 tons [Sensors]", mekLocation.getDetails(false)); @@ -2054,7 +2053,7 @@ void getDetailsSpareTest() { assertNotNull(mekLocation.getDetails()); assertTrue(mekLocation.getDetails().startsWith("25 tons")); - assertTrue(mekLocation.getDetails().contains("(100%)")); + //assertTrue(mekLocation.getDetails().contains("(100%)")); assertTrue(mekLocation.getDetails().contains("[Sensors, Life Support]")); assertNotNull(mekLocation.getDetails(false)); assertEquals("25 tons [Sensors, Life Support]", mekLocation.getDetails(false)); @@ -2075,39 +2074,39 @@ void getDetailsOnUnitTest() { mekLocation.setUnit(unit); assertNotNull(mekLocation.getDetails()); - assertEquals("Right Arm (100%)", mekLocation.getDetails()); + assertEquals("Right Arm, 30 tons", mekLocation.getDetails()); assertNotNull(mekLocation.getDetails(false)); - assertEquals("Right Arm", mekLocation.getDetails(false)); + assertEquals("Right Arm, 30 tons", mekLocation.getDetails(false)); mekLocation.setPercent(0.1); assertNotNull(mekLocation.getDetails()); - assertEquals("Right Arm (10%)", mekLocation.getDetails()); + assertEquals("Right Arm, 30 tons (10%)", mekLocation.getDetails()); assertNotNull(mekLocation.getDetails(false)); - assertEquals("Right Arm", mekLocation.getDetails(false)); + assertEquals("Right Arm, 30 tons", mekLocation.getDetails(false)); mekLocation.setBlownOff(true); assertNotNull(mekLocation.getDetails()); - assertEquals("Right Arm (Blown Off)", mekLocation.getDetails()); + assertEquals("Right Arm, 30 tons (Blown Off)", mekLocation.getDetails()); assertNotNull(mekLocation.getDetails(false)); - assertEquals("Right Arm", mekLocation.getDetails(false)); + assertEquals("Right Arm, 30 tons", mekLocation.getDetails(false)); mekLocation.setBlownOff(false); mekLocation.setBreached(true); assertNotNull(mekLocation.getDetails()); - assertEquals("Right Arm (Breached)", mekLocation.getDetails()); + assertEquals("Right Arm, 30 tons (Breached)", mekLocation.getDetails()); assertNotNull(mekLocation.getDetails(false)); - assertEquals("Right Arm", mekLocation.getDetails(false)); + assertEquals("Right Arm, 30 tons", mekLocation.getDetails(false)); mekLocation.setBreached(false); doReturn(true).when(unit).hasBadHipOrShoulder((mekLocation.getLoc())); assertNotNull(mekLocation.getDetails()); - assertEquals("Right Arm (Bad Hip/Shoulder)", mekLocation.getDetails()); + assertEquals("Right Arm, 30 tons (Bad Hip/Shoulder)", mekLocation.getDetails()); assertNotNull(mekLocation.getDetails(false)); - assertEquals("Right Arm", mekLocation.getDetails(false)); + assertEquals("Right Arm, 30 tons", mekLocation.getDetails(false)); } @Test From 6ffa598c399051a1572ebd3a904c3d05c6e0a410 Mon Sep 17 00:00:00 2001 From: Weaver Date: Fri, 18 Oct 2024 22:15:34 -0700 Subject: [PATCH 057/118] Change "Clear All Items" to "Remove Selected Items" on CC Tab List --- MekHQ/resources/mekhq/resources/GUI.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MekHQ/resources/mekhq/resources/GUI.properties b/MekHQ/resources/mekhq/resources/GUI.properties index fef3b0001a..42f9f15646 100644 --- a/MekHQ/resources/mekhq/resources/GUI.properties +++ b/MekHQ/resources/mekhq/resources/GUI.properties @@ -268,8 +268,8 @@ addMinimumComplementGreen.text=Green addMinimumComplementUltraGreen.text=Ultra Green #### ProcurementTableMouseAdapter Class -miClearItems.text=Clear All Items -miClearItems.toolTipText=This immediately removes all selected items from the procurement list, as well as any items currently showing as zero left to acquire. +miClearItems.text=Remove Selected Items +miClearItems.toolTipText=This immediately removes all selected items from the procurement list. miProcureSingleItemImmediately.text=Procure a Single Item Immediately miProcureSingleItemImmediately.toolTipText=This immediately attempts to procure an item for each selected row as if an administrator had successfully rolled to acquire the item. miAddSingleItemImmediately.text=Add a Single Item Immediately From d83f1e17ac2b22ed1a3c7e2e0032c1ce3a7e1d50 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 11:36:31 -0500 Subject: [PATCH 058/118] Set RetirementDefectionDialog always on top Removed redundant visibility and location setting for RetirementDefectionDialog in multiple classes and set it to always be on top within the dialog initialization. This ensures the dialog appears correctly without requiring additional location settings. --- MekHQ/src/mekhq/MekHQ.java | 2 - MekHQ/src/mekhq/gui/BriefingTab.java | 8 ++-- MekHQ/src/mekhq/gui/CampaignGUI.java | 4 +- .../adapter/PersonnelTableMouseAdapter.java | 2 +- .../gui/dialog/RetirementDefectionDialog.java | 42 ++++++++----------- 5 files changed, 24 insertions(+), 34 deletions(-) diff --git a/MekHQ/src/mekhq/MekHQ.java b/MekHQ/src/mekhq/MekHQ.java index cd1524a59e..45c0ca79c7 100644 --- a/MekHQ/src/mekhq/MekHQ.java +++ b/MekHQ/src/mekhq/MekHQ.java @@ -515,8 +515,6 @@ public void gameVictory(GameVictoryEvent gve) { if (!getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty()) { RetirementDefectionDialog rdd = new RetirementDefectionDialog(campaignGUI, campaignGUI.getCampaign().getMission(currentScenario.getMissionId()), false); - rdd.setLocation(rdd.getLocation().x, 0); - rdd.setVisible(true); if (!rdd.wasAborted()) { getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments()); diff --git a/MekHQ/src/mekhq/gui/BriefingTab.java b/MekHQ/src/mekhq/gui/BriefingTab.java index b420870c1f..7e7dc0cc66 100644 --- a/MekHQ/src/mekhq/gui/BriefingTab.java +++ b/MekHQ/src/mekhq/gui/BriefingTab.java @@ -439,8 +439,8 @@ private void completeMission() { if ((getCampaign().getCampaignOptions().isUseRandomRetirement()) && (getCampaign().getCampaignOptions().isUseContractCompletionRandomRetirement())) { RetirementDefectionDialog rdd = new RetirementDefectionDialog(getCampaignGui(), mission, true); - rdd.setLocation(rdd.getLocation().x, 0); - rdd.setVisible(true); + + if (rdd.wasAborted()) { /* @@ -727,8 +727,8 @@ private void resolveScenario() { if (!getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty()) { RetirementDefectionDialog rdd = new RetirementDefectionDialog(getCampaignGui(), getCampaign().getMission(scenario.getMissionId()), false); - rdd.setLocation(rdd.getLocation().x, 0); - rdd.setVisible(true); + + if (!rdd.wasAborted()) { getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments()); diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index e570d55a8a..196924dda7 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -1222,8 +1222,8 @@ public boolean showRetirementDefectionDialog() { */ RetirementDefectionDialog rdd = new RetirementDefectionDialog(this, null, getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty()); - rdd.setLocation(rdd.getLocation().x, 0); - rdd.setVisible(true); + + if (!rdd.wasAborted()) { getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments()); diff --git a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java index 9e4a0f45e5..b4521be048 100644 --- a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java @@ -824,7 +824,7 @@ public void actionPerformed(ActionEvent action) { if (showDialog) { RetirementDefectionDialog rdd = new RetirementDefectionDialog( gui, null, false); - rdd.setVisible(true); + if (rdd.wasAborted() || !gui.getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments())) { diff --git a/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java b/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java index 599d9c501d..e14ff89566 100644 --- a/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java @@ -18,31 +18,6 @@ */ package mekhq.gui.dialog; -import static mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker.Payout.isBreakingContract; - -import java.awt.BorderLayout; -import java.awt.CardLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.FlowLayout; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.time.temporal.ChronoUnit; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.ResourceBundle; -import java.util.UUID; - -import javax.swing.*; -import javax.swing.JSpinner.DefaultEditor; -import javax.swing.RowSorter.SortKey; -import javax.swing.table.TableCellEditor; -import javax.swing.table.TableColumn; -import javax.swing.table.TableRowSorter; - import megamek.client.ui.models.XTableColumnModel; import megamek.client.ui.preferences.JComboBoxPreference; import megamek.client.ui.preferences.JIntNumberSpinnerPreference; @@ -68,6 +43,21 @@ import mekhq.gui.sorter.PersonRankStringSorter; import mekhq.gui.sorter.WeightClassSorter; +import javax.swing.*; +import javax.swing.JSpinner.DefaultEditor; +import javax.swing.RowSorter.SortKey; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableColumn; +import javax.swing.table.TableRowSorter; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.*; + +import static mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker.Payout.isBreakingContract; + /** * @author Neoancient */ @@ -141,6 +131,8 @@ public RetirementDefectionDialog(CampaignGUI gui, Mission mission, boolean doRet setLocationRelativeTo(gui.getFrame()); setUserPreferences(doRetirement); + setAlwaysOnTop(true); + setVisible(true); } private void initComponents(boolean doRetirement) { From 1377c7cf28da42e3466d64afe47d2e82ceea91a0 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 11:37:32 -0500 Subject: [PATCH 059/118] Refactor: use UIUtil for scaling dialog dimensions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated RetirementDefectionDialog to use UIUtil.scaleForGUI for setting dialog dimensions to ensure consistency with other UI components and improve scaling across different display settings. This change helps maintain a uniform look and feel in the application’s UI. --- MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java b/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java index e14ff89566..cd0b5c0460 100644 --- a/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java @@ -23,6 +23,7 @@ import megamek.client.ui.preferences.JIntNumberSpinnerPreference; import megamek.client.ui.preferences.JWindowPreference; import megamek.client.ui.preferences.PreferencesNode; +import megamek.client.ui.swing.util.UIUtil; import megamek.common.Entity; import megamek.common.TargetRoll; import megamek.common.TechConstants; @@ -122,7 +123,7 @@ public RetirementDefectionDialog(CampaignGUI gui, Mission mission, boolean doRet targetRolls = rdTracker.getTargetNumbers(mission, hqView.getCampaign()); } currentPanel = doRetirement ? PAN_OVERVIEW : PAN_RESULTS; - setSize(new Dimension(800, 600)); + setSize(UIUtil.scaleForGUI(800, 600)); initComponents(doRetirement); if (!doRetirement) { initResults(); From daa560325511ed124f8b7e25adc69f8c33f8251f Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 12:20:43 -0500 Subject: [PATCH 060/118] Add check for no eligible retirements in RetirementDefectionDialog Implemented a check in RetirementDefectionDialog to display a notification when no personnel are eligible for retirement or defection. This includes a new dialog with a message and an OK button to acknowledge the notification. --- .../RetirementDefectionDialog.properties | 3 + MekHQ/src/mekhq/gui/BriefingTab.java | 2 - MekHQ/src/mekhq/gui/CampaignGUI.java | 6 +- .../gui/dialog/RetirementDefectionDialog.java | 64 +++++++++++++++++++ 4 files changed, 69 insertions(+), 6 deletions(-) diff --git a/MekHQ/resources/mekhq/resources/RetirementDefectionDialog.properties b/MekHQ/resources/mekhq/resources/RetirementDefectionDialog.properties index 9d25586381..86c8a0295f 100644 --- a/MekHQ/resources/mekhq/resources/RetirementDefectionDialog.properties +++ b/MekHQ/resources/mekhq/resources/RetirementDefectionDialog.properties @@ -20,3 +20,6 @@ txtInstructions.Overview.text=The turnover check involves rolling 2d6 and compar txtInstructions.Results.text=Combat personnel who brought their units with them should be given a unit upon departure. If no unit of the appropriate weight class and technology is available, the retiree is compensated in C-bills.\ \n\ \nAny pilots with a final payout value of zero have either been compensated with a unit that meets or exceeds the payout amount, or are breaking their employment contract. + +nobodyEligibleDialog.title=Turnover & Retention +nobodyEligibleDialog.text=Nobody wants to leave the unit at this time. diff --git a/MekHQ/src/mekhq/gui/BriefingTab.java b/MekHQ/src/mekhq/gui/BriefingTab.java index 7e7dc0cc66..d2a6709fc6 100644 --- a/MekHQ/src/mekhq/gui/BriefingTab.java +++ b/MekHQ/src/mekhq/gui/BriefingTab.java @@ -440,8 +440,6 @@ private void completeMission() { && (getCampaign().getCampaignOptions().isUseContractCompletionRandomRetirement())) { RetirementDefectionDialog rdd = new RetirementDefectionDialog(getCampaignGui(), mission, true); - - if (rdd.wasAborted()) { /* * Once the retirement rolls have been made, the outstanding payouts can be diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index 196924dda7..283833c479 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -1220,10 +1220,8 @@ public boolean showRetirementDefectionDialog() { * present the retirement view to give the player a chance to follow a * custom schedule */ - RetirementDefectionDialog rdd = new RetirementDefectionDialog(this, null, - getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty()); - - + boolean doRetirement = getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty(); + RetirementDefectionDialog rdd = new RetirementDefectionDialog(this, null, doRetirement); if (!rdd.wasAborted()) { getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments()); diff --git a/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java b/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java index cd0b5c0460..2587831b00 100644 --- a/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java @@ -30,12 +30,14 @@ import megamek.common.UnitType; import megamek.logging.MMLogger; import mekhq.MekHQ; +import mekhq.campaign.Campaign; import mekhq.campaign.finances.Money; import mekhq.campaign.finances.enums.TransactionType; import mekhq.campaign.mission.Mission; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker; import mekhq.campaign.unit.Unit; +import mekhq.campaign.universe.Factions; import mekhq.gui.CampaignGUI; import mekhq.gui.enums.PersonnelFilter; import mekhq.gui.model.RetirementTableModel; @@ -121,6 +123,14 @@ public RetirementDefectionDialog(CampaignGUI gui, Mission mission, boolean doRet rdTracker = hqView.getCampaign().getRetirementDefectionTracker(); if (doRetirement) { targetRolls = rdTracker.getTargetNumbers(mission, hqView.getCampaign()); + + if (targetRolls.isEmpty()) { + aborted = false; + setVisible(false); + + nobodyEligibleDialog(gui, gui.getCampaign()); + return; + } } currentPanel = doRetirement ? PAN_OVERVIEW : PAN_RESULTS; setSize(UIUtil.scaleForGUI(800, 600)); @@ -755,6 +765,17 @@ public Map getUnitAssignments() { return unitAssignments; } + /** + * Returns a {@link Map} containing the {@link TargetRoll} objects associated with the {@link UUID} keys. + *

+ * If this returns empty, it means nobody is eligible for retirement/resignation + * + * @return a {@link Map} of {@link UUID} keys mapped to {@link TargetRoll} values + */ + public Map getTargetRolls() { + return targetRolls; + } + public boolean wasAborted() { return aborted; } @@ -889,6 +910,49 @@ private void setUnitGroup() { filterUnits(); } } + + /** + * Creates and displays a dialog showing that no members of the campaign have a turnover target + * number greater than 2. The dialog includes a scaled faction logo, a message, and an 'OK' + * button to close the dialog. + * + * @param gui the {@link CampaignGUI} object providing context for the dialog. + * @param campaign the current {@link Campaign}. + */ + private void nobodyEligibleDialog(CampaignGUI gui, Campaign campaign) { + // Main frame for the test + JFrame frame = gui.getFrame(); + + // Creating an instance of JDialog + JDialog dialog = new JDialog(frame, resourceMap.getString("nobodyEligibleDialog.title"), true); + + // Setting the layout + dialog.setLayout(new BorderLayout()); + + // Creating and scaling the image label + ImageIcon originalIcon = Factions.getFactionLogo(campaign, campaign.getFaction().getShortName(), + true); + ImageIcon scaledIcon = new ImageIcon(originalIcon.getImage().getScaledInstance( + originalIcon.getIconWidth()/2, originalIcon.getIconHeight()/2, Image.SCALE_FAST)); + JLabel imageLabel = new JLabel(scaledIcon); + + dialog.add(imageLabel, BorderLayout.NORTH); + + // Dialog body + JLabel text = new JLabel(resourceMap.getString("nobodyEligibleDialog.text"), JLabel.CENTER); + dialog.add(text, BorderLayout.CENTER); + + // Options + JButton okButton = new JButton(resourceMap.getString("btnDone.text")); + okButton.addActionListener(e -> dialog.dispose()); + dialog.add(okButton, BorderLayout.SOUTH); + + // Formating + dialog.setSize(UIUtil.scaleForGUI(400, 200)); + dialog.setLocationRelativeTo(gui.getFrame()); + dialog.setAlwaysOnTop(true); + dialog.setVisible(true); + } } class RetirementTable extends JTable { From 64862dd6a211e624c52b744e25d296eddbfc791e Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 12:21:31 -0500 Subject: [PATCH 061/118] Refactor variable name in RetirementDefectionDialog code Renamed the variable 'rdd' to 'dialog' for better readability and consistency across the BriefingTab and CampaignGUI classes. This change helps to maintain code clarity and aids in understanding the purpose of the variable more intuitively. --- MekHQ/src/mekhq/gui/BriefingTab.java | 8 +++----- MekHQ/src/mekhq/gui/CampaignGUI.java | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/MekHQ/src/mekhq/gui/BriefingTab.java b/MekHQ/src/mekhq/gui/BriefingTab.java index d2a6709fc6..699cf93e27 100644 --- a/MekHQ/src/mekhq/gui/BriefingTab.java +++ b/MekHQ/src/mekhq/gui/BriefingTab.java @@ -723,13 +723,11 @@ private void resolveScenario() { resolveDialog.setVisible(true); if (!getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty()) { - RetirementDefectionDialog rdd = new RetirementDefectionDialog(getCampaignGui(), + RetirementDefectionDialog dialog = new RetirementDefectionDialog(getCampaignGui(), getCampaign().getMission(scenario.getMissionId()), false); - - - if (!rdd.wasAborted()) { - getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments()); + if (!dialog.wasAborted()) { + getCampaign().applyRetirement(dialog.totalPayout(), dialog.getUnitAssignments()); } } diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index 283833c479..47ee5d92b1 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -1221,10 +1221,10 @@ public boolean showRetirementDefectionDialog() { * custom schedule */ boolean doRetirement = getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty(); - RetirementDefectionDialog rdd = new RetirementDefectionDialog(this, null, doRetirement); + RetirementDefectionDialog dialog = new RetirementDefectionDialog(this, null, doRetirement); - if (!rdd.wasAborted()) { - getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments()); + if (!dialog.wasAborted()) { + getCampaign().applyRetirement(dialog.totalPayout(), dialog.getUnitAssignments()); return true; } else { return false; From 4a9a5d84280d96a870f04684c11515ce4b392415 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 13:14:38 -0500 Subject: [PATCH 062/118] Added `TotalGenericBattleValue` Tag to MUL Export Refactored the `saveTo` method in `EntityListFile` to include an optional generic battle value parameter. Added calculation and integration of generic battle value in `BriefingTab` when saving entity lists to files. This is hidden in MUL export as GBV is not meant to be user-facing. We include it here as a debugging tool. --- MekHQ/src/mekhq/gui/BriefingTab.java | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/gui/BriefingTab.java b/MekHQ/src/mekhq/gui/BriefingTab.java index b420870c1f..46883437eb 100644 --- a/MekHQ/src/mekhq/gui/BriefingTab.java +++ b/MekHQ/src/mekhq/gui/BriefingTab.java @@ -1146,10 +1146,13 @@ private void deployListFile() { chosen.clear(); chosen.addAll(((AtBScenario) scenario).getAlliesPlayer()); file = determineMULFilePath(scenario, ((AtBContract) mission).getEmployer()); + + int genericBattleValue = calculateGenericBattleValue(chosen); + if (file != null) { try { // Save the player's allied entities to the file. - EntityListFile.saveTo(file, chosen); + EntityListFile.saveTo(file, chosen, genericBattleValue); } catch (Exception ex) { logger.error("", ex); } @@ -1164,10 +1167,13 @@ private void deployListFile() { continue; } file = determineMULFilePath(scenario, botForce.getName()); + + int genericBattleValue = calculateGenericBattleValue(chosen); + if (file != null) { try { // Save the bot force's entities to the file. - EntityListFile.saveTo(file, chosen); + EntityListFile.saveTo(file, chosen, genericBattleValue); } catch (Exception ex) { logger.error("", ex); } @@ -1175,6 +1181,24 @@ private void deployListFile() { } } + /** + * Calculates the total generic battle value of the entities chosen. + * If the use of generic battle value option is enabled in the campaign options, the generic battle + * value of each entity in the list is summed up and returned as the total generic battle value. + * If the said option is disabled, the method returns 0. + * + * @param chosen the list of entities for which the generic battle value is to be calculated. + * @return the total generic battle value or 0 if the generic battle value usage is turned off in + * campaign options. + */ + private int calculateGenericBattleValue(ArrayList chosen) { + int genericBattleValue = 0; + if (getCampaign().getCampaignOptions().isUseGenericBattleValue()) { + genericBattleValue = chosen.stream().mapToInt(Entity::getGenericBattleValue).sum(); + } + return genericBattleValue; + } + private @Nullable File determineMULFilePath(final Scenario scenario, final String name) { final Optional maybeUnitFile = FileDialogs.saveDeployUnits(getFrame(), scenario, name); if (maybeUnitFile.isEmpty()) { From a3394620cb3c06e792bf38f860c125c3d89de2c6 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 13:39:18 -0500 Subject: [PATCH 063/118] Fixed Hire Minimum Compliment Commands Standardized the GM hire command naming for consistency and fixed the skill level override logic to skip pre-existing crew members. This prevents unintended skill level changes and ensures correct hiring and configuration of personnel. --- .../gui/adapter/UnitTableMouseAdapter.java | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java index 8432d306a0..8a13f2a3f2 100644 --- a/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/UnitTableMouseAdapter.java @@ -127,7 +127,7 @@ public class UnitTableMouseAdapter extends JPopupMenuAdapter { public static final String COMMAND_GM_MOTHBALL = COMMAND_MOTHBALL + COMMAND_GM; public static final String COMMAND_GM_ACTIVATE = COMMAND_ACTIVATE + COMMAND_GM; public static final String COMMAND_UNDEPLOY = "UNDEPLOY"; - public static final String COMMAND_HIRE_FULL_GM_RANDOM = COMMAND_HIRE_FULL + COMMAND_GM; + public static final String COMMAND_HIRE_FULL_GM = COMMAND_HIRE_FULL + COMMAND_GM; public static final String COMMAND_HIRE_FULL_GM_ELITE = COMMAND_HIRE_FULL + COMMAND_GM + "ELITE"; public static final String COMMAND_HIRE_FULL_GM_VETERAN = COMMAND_HIRE_FULL + COMMAND_GM + "VETERAN"; public static final String COMMAND_HIRE_FULL_GM_REGULAR = COMMAND_HIRE_FULL + COMMAND_GM + "REGULAR"; @@ -332,29 +332,40 @@ public void actionPerformed(ActionEvent action) { } } else if (command.contains(COMMAND_HIRE_FULL)) { boolean isGM = command.contains("GM"); + HirePersonnelUnitAction hireAction = new HirePersonnelUnitAction(isGM); + for (Unit unit : units) { + List preExistingCrew = unit.getCrew(); + hireAction.execute(gui.getCampaign(), unit); - if (command.contains("RANDOM")) { - continue; - } + boolean fixSkillLevels = false; SkillLevel skillLevel = SkillLevel.REGULAR; if (command.contains("ELITE")) { skillLevel = SkillLevel.ELITE; + fixSkillLevels = true; } else if (command.contains("VETERAN")) { skillLevel = SkillLevel.VETERAN; - } else if (command.contains("GREEN")) { - skillLevel = SkillLevel.GREEN; + fixSkillLevels = true; } else if (command.contains("ULTRA_GREEN")) { skillLevel = SkillLevel.ULTRA_GREEN; + fixSkillLevels = true; + } else if (command.contains("GREEN")) { + skillLevel = SkillLevel.GREEN; + fixSkillLevels = true; } - for (Person person : unit.getCrew()) { - overrideSkills(gui.getCampaign(), person, person.getPrimaryRole(), skillLevel.ordinal()); - } + if (fixSkillLevels) { + for (Person person : unit.getCrew()) { + if (preExistingCrew.contains(person)) { + continue; + } + overrideSkills(gui.getCampaign(), person, person.getPrimaryRole(), skillLevel.ordinal()); + } + } } } else if (command.equals(COMMAND_CUSTOMIZE)) { // Single Unit only ((MekLabTab) gui.getTab(MHQTabType.MEK_LAB)).loadUnit(selectedUnit); @@ -1008,7 +1019,7 @@ protected Optional createPopupMenu() { JMenu menuMinimumComplement = new JMenu(resources.getString("addMinimumComplement.text")); menuItem = new JMenuItem(resources.getString("addMinimumComplementRandom.text")); - menuItem.setActionCommand(COMMAND_HIRE_FULL_GM_RANDOM); + menuItem.setActionCommand(COMMAND_HIRE_FULL_GM); menuItem.addActionListener(this); menuMinimumComplement.add(menuItem); @@ -1096,10 +1107,10 @@ private void addCustomUnitTag(Unit... units) { Entity entity = unit.getEntity(); String unitName = entity.getShortNameRaw(); String fileExtension = entity instanceof Mek ? ".mtf" : ".blk"; - String fileOutName = MHQConstants.CUSTOM_MEKFILES_DIRECTORY_PATH + File.separator + String fileOutName = MHQConstants.CUSTOM_MEKFILES_DIRECTORY_PATH + File.separator + unitName + fileExtension; String fileNameCampaign = sCustomsDirCampaign + File.separator + unitName + fileExtension; - + // if this file already exists then don't overwrite it or we will end up with a bunch of copies if ((new File(fileOutName)).exists() || (new File(fileNameCampaign)).exists()) { JOptionPane.showMessageDialog(null, @@ -1107,7 +1118,7 @@ private void addCustomUnitTag(Unit... units) { "File Already Exists", JOptionPane.ERROR_MESSAGE); return; } - + if (entity instanceof Mek) { try (OutputStream os = new FileOutputStream(fileNameCampaign); PrintStream p = new PrintStream(os)) { From f4ebf8193ac0ca0587d8e9b6f92f7f594450a26b Mon Sep 17 00:00:00 2001 From: Daniel L- <103902653+IllianiCBT@users.noreply.github.com> Date: Sat, 19 Oct 2024 14:37:20 -0500 Subject: [PATCH 064/118] Update history.txt --- MekHQ/docs/history.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/MekHQ/docs/history.txt b/MekHQ/docs/history.txt index 0d9267b741..dcc43d3ff7 100644 --- a/MekHQ/docs/history.txt +++ b/MekHQ/docs/history.txt @@ -108,6 +108,7 @@ MEKHQ VERSION HISTORY: + PR #5053: Colorise Skill Levels + PR #5054: Implement CamOps Contract Negotiation + PR #5055: Parts In Use - Filter Mothballed and Spare Part Quality ++ PR #5064: Bug fix and improvements for calculateContractDifficulty() 0.50.0 (2024-09-01 2000 UTC) (THIS MARKS THE START OF JAVA 17 AS THE MINIMUM REQUIRED) + PR #4332: CI Updates for windows build and normalizing From f4144392f9a468717803c0dff4251a8f6ddf2fc6 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 14:53:06 -0500 Subject: [PATCH 065/118] Updated contract difficulty visualization and calculation Revised contract difficulty stars to use skull images and improved the difficulty calculation logic. Refined the calculation to provide a more accurate difficulty representation, ensuring better gameplay balance and clarity. --- .../images/misc/challenge_estimate_full.png | Bin 0 -> 1838 bytes .../images/misc/challenge_estimate_half.png | Bin 0 -> 1090 bytes .../factions/logo_star_league_green.png | Bin 1046 -> 0 bytes .../factions/logo_star_league_orange.png | Bin 1046 -> 0 bytes .../factions/logo_star_league_red.png | Bin 1046 -> 0 bytes .../mekhq/campaign/mission/AtBContract.java | 51 ++++++++++-------- 6 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 MekHQ/data/images/misc/challenge_estimate_full.png create mode 100644 MekHQ/data/images/misc/challenge_estimate_half.png delete mode 100644 MekHQ/data/images/universe/factions/logo_star_league_green.png delete mode 100644 MekHQ/data/images/universe/factions/logo_star_league_orange.png delete mode 100644 MekHQ/data/images/universe/factions/logo_star_league_red.png diff --git a/MekHQ/data/images/misc/challenge_estimate_full.png b/MekHQ/data/images/misc/challenge_estimate_full.png new file mode 100644 index 0000000000000000000000000000000000000000..76cdc939c46c7a452430fc26a4e48b44fa65352d GIT binary patch literal 1838 zcmV+}2hsS6P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91D4+uX1ONa40RR91C;$Ke0D9(TtN;K8m`OxIR9FeUm}^X2RS}WJ`sBEnBwi zjf#pojKU){|D`G_a!zpX^z`%>b#-;82L}g-m!iKE18q4uIR~Soqd#K8l7(U_fzkah0E+UzXJ*_c1nDR#ujb{;1g;RtEZ&l$121_^6rk1LFUK zk(p+5`I-%E-@g4%Utiyq@|KBFE+8P_NiABz{) z)2XScj~lIFWS~7UG4UHC;g#e?L`38m&2MC&$eC`v(IgLoXSdr!jpi{ja1KFfs|st^ zu6>sUz3lN~AQ*;V@TyujzgQy3Prt60fhj2|ZzE)te)My!Xy3~~Qj23nSG1m7u0s#- zGO((u>Qf|EJqB4*t~@X2cp0b&PJveyxSRiDK`+O61tmg~Vhqy^leAJgy~p@n$}K6e zcF`>36`eN9tas+-=0*sg&FJk%XC2K0sBX@AFqDqrkFaV*W&xlkuxpuf_}y4-aa1F4Aa= zeXd`>{;F0GFPz7?*WtO1lkxWU_8u3UA3Asvd2#=m$81bY`4Qy*`t7f4t5$D??9TecH z>gw4D{8xnP?CdO3x2dC}L&Cvl+qP}L3DyJEOC-D`nzNyyL8=(jtYK=PC)0Lwm|&va+(8Z{NPXKR7rzjx9QYjO!2Hx^?SIMP9rtys3eKf#byI z=g6Mq&mC08Wj=T83GdFGJExT~LuBPaQP6+T*zw#MjC-Fkw=SnuJmwP~9-gvw>(-M( z*VNQ}1)3HbNK~4KaWcTJd?45i+>4k9gq!d^*hxuA*)aTrf`UTUtXcCKbVz!7dOldb zlP!VpQ2&IXoaGjCI4qUb88>d+c#$Ug&J_}D>6l8ZGG=&ka`G3mG2t6tX{rJSmJ>&r z9F|^a@wVhVGtkDXmiCZmuD@Z(;p-kBAOG&kl`AJa1auwKiF9(VF7fgZ2jvesk{Xr` zLqkKytE;OmV@^NmVln6;-{0Tg+tk#Q2h%Y-JNvW8;6i5xQhU7-W{z5lftPRGxbXvH zmbqhxnd7f}s`g?5(Y>&+~5c6qU;}obn!SkuLx!S}q zSAhYZ#ITEat(FIXCqz9vJ9{4}7#-L3DZB>`VPRo$q!4F8L4nkGKE=hw9||V-MF|EZ zk*m4WwL4sgA`>inGv+oj zT|Z{l$8(LfwYBT0)Nh)}2!HqP-LkG2=MDq6r6K^htmnk}vemQmEc0LF4v-5^DSAeN z6O>{v@FsoUXi&%VFiaiO8>r{I|A{)F$9&mkau|;3`FPDyUtfO!kDG;yUge(qF0bCR c_I)b;5ALN*ifs{6$N&HU07*qoM6N<$f>bkWM*si- literal 0 HcmV?d00001 diff --git a/MekHQ/data/images/misc/challenge_estimate_half.png b/MekHQ/data/images/misc/challenge_estimate_half.png new file mode 100644 index 0000000000000000000000000000000000000000..8356fbaf2e84d6181f41788de92c099406fdf7ee GIT binary patch literal 1090 zcmV-I1ikx-P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR916rckD1ONa40RR91C;$Ke0M-npdH?_etVu*cR7ee-R!dA%K@{!l4_b;) zTELcXq>7|y(iEZ?jMxMsD;F+E)Qw@K#uz^;ke~^Wz{2ne z@(HHa(iCDLV1e4!b4%Z2d~fAgyh$; zLHX!B!XyZ&@2e>Y%gf6xU^4BC4Il+!eSQ5c*wxBO^;-%;olcifMdWbuGBKf`*MU=f zEC*NG%>%{Y+1v}57sDf@lHrz3DwSFYhr>PS7ZCFrJtZgA%MBPOlF8(#R;xYD`nI>X z`v(UHufrym)SJ4xI!9w;;{wh5-u(-jv~t- z1q*_37{ZtVvO+-JULivu5IDQKy81gSA!5Ln3S`j7uui{lEM1wg#%{MeJ3Bik3$P1rKP77jAqP5NJY*zEi&t8 z4Wv&_P7aVPE-uc#h7^ESyd%_qtE;QMh%giu7Mj^9DNtCWuh@`N3J2dnr$*o&F)9E2}2?tkH3%{ zjFJ~&cX#(~hr{8C#bPf}_;j;tKuquV;=;nhImk#}gg9ESu$I=Lp`ptK1qDZmFdB`1 zKw*dG=H`faJ69bNMrz0=R$5y690q$R6#BNYu~CILd>zN{Dr|JJ*b7C%WmPN|O9hT# zs=dAa2?hClzK0}BN=mFG(X^^%vlFxXi*swKt*yNadBSG1{WP1+k1&p-znh<*?`vyo zdmxn!zI*VNF^a8GVm8>h2Y3t)Z!00-#x{vHI5?EnA(07*qo IM6N<$f(@D1{r~^~ literal 0 HcmV?d00001 diff --git a/MekHQ/data/images/universe/factions/logo_star_league_green.png b/MekHQ/data/images/universe/factions/logo_star_league_green.png deleted file mode 100644 index 08fcb463d1b32fea539370edc42f60e74ee87e04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1046 zcmV+x1nK*UP)HtU~cBP6K#WDBZ+0Oll9mUE3QlnP6fKb6R9XL(*E57!iM%Yg^YS$*N36|+LVYf4jm+P zlBgsxk+etR=~0M*tOcZZ6ljF>j)5Hep(Fc^#pjX7h)Z;m95C|!l@#?rTrYjg$mw6JK~oebBkBF~n^S_OC^&!(pZcaiz!c?6 z3ZMJ?djesmDC9dEWFGqy5v8t&CRxuq3Ph4pZpLKfKs3a@l#${)hmlOCb|AlnB~b2^ z6W3>{EiId(b4a}FuZ#u2wp#ii5O)ghp?C<)Z$r^L;L^g^RxJ2BbO@dg>@_iit&;G7 zoN=akWMO@i;-GAB1OrhZMFjJDK;j3(Jl@Kv=NSw!dbTMTA!S@gw>rOCf{|jx+>l4S zkS22eC>55+FXWt7LyA0=Qy5=Fb_BUCtC#wEKqv3sp!rD6ZDZVrUBM;_vnPHXVcu4IiRub;_D1NZhoC83Pa|0iwTrFxVCdVZgtWd5y&j3y>p>rIm8R+g&=j1g zngVnPSA(h!0qw%IrK(*(@8n8V(L11#a^0(FB+ykkQFPU#FQic3ymC!FN=Thruf7#D zxntDaaw=~|H8-#Z_qm#TW9415`cB*AhFpCUufDxk|4Pv4HwW!;P5y227vazVVF^3} QZ2$lO07*qoM6N<$f>k}>egFUf diff --git a/MekHQ/data/images/universe/factions/logo_star_league_orange.png b/MekHQ/data/images/universe/factions/logo_star_league_orange.png deleted file mode 100644 index 47119861efd20c973019148950321ff5096dc624..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1046 zcmV+x1nK*UP)PJZ00002bW%=J0RLN& zBDDYj1Di=iK~#90?U=uB(?Ar*@8Y5uRn$cpV5n*%hO)uf0V!pIt$zaIKVSq2)lNYw zTlo+AH}DtKl^D7KYb8`W>HtU~cBP6K#WDBZ+0Oll9mUE3QlnP6fKb6R9XL(*E57!iM%Yg^YS$*N36|+LVYf4jm+P zlBgsxk+etR=~0M*tOcZZ6ljF>j)5Hep(Fc^#pjX7h)Z;m95C|!l@#?rTrYjg$mw6JK~oebBkBF~n^S_OC^&!(pZcaiz!c?6 z3ZMJ?djesmDC9dEWFGqy5v8t&CRxuq3Ph4pZpLKfKs3a@l#${)hmlOCb|AlnB~b2^ z6W3>{EiId(b4a}FuZ#u2wp#ii5O)ghp?C<)Z$r^L;L^g^RxJ2BbO@dg>@_iit&;G7 zoN=akWMO@i;-GAB1OrhZMFjJDK;j3(Jl@Kv=NSw!dbTMTA!S@gw>rOCf{|jx+>l4S zkS22eC>55+FXWt7LyA0=Qy5=Fb_BUCtC#wEKqv3sp!rD6ZDZVrUBM;_vnPHXVcu4IiRub;_D1NZhoC83Pa|0iwTrFxVCdVZgtWd5y&j3y>p>rIm8R+g&=j1g zngVnPSA(h!0qw%IrK(*(@8n8V(L11#a^0(FB+ykkQFPU#FQic3ymC!FN=Thruf7#D zxntDaaw=~|H8-#Z_qm#TW9415`cB*AhFpCUufDxk|4Pv4HwW!;P5y227vazVVF^3} QZ2$lO07*qoM6N<$fn+a diff --git a/MekHQ/data/images/universe/factions/logo_star_league_red.png b/MekHQ/data/images/universe/factions/logo_star_league_red.png deleted file mode 100644 index f3061742cd4fefe4bdbecdae0074498391f5341d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1046 zcmV+x1nK*UP)HtU~cBP6K#WDBZ+0Oll9mUE3QlnP6fKb6R9XL(*E57!iM%Yg^YS$*N36|+LVYf4jm+P zlBgsxk+etR=~0M*tOcZZ6ljF>j)5Hep(Fc^#pjX7h)Z;m95C|!l@#?rTrYjg$mw6JK~oebBkBF~n^S_OC^&!(pZcaiz!c?6 z3ZMJ?djesmDC9dEWFGqy5v8t&CRxuq3Ph4pZpLKfKs3a@l#${)hmlOCb|AlnB~b2^ z6W3>{EiId(b4a}FuZ#u2wp#ii5O)ghp?C<)Z$r^L;L^g^RxJ2BbO@dg>@_iit&;G7 zoN=akWMO@i;-GAB1OrhZMFjJDK;j3(Jl@Kv=NSw!dbTMTA!S@gw>rOCf{|jx+>l4S zkS22eC>55+FXWt7LyA0=Qy5=Fb_BUCtC#wEKqv3sp!rD6ZDZVrUBM;_vnPHXVcu4IiRub;_D1NZhoC83Pa|0iwTrFxVCdVZgtWd5y&j3y>p>rIm8R+g&=j1g zngVnPSA(h!0qw%IrK(*(@8n8V(L11#a^0(FB+ykkQFPU#FQic3ymC!FN=Thruf7#D zxntDaaw=~|H8-#Z_qm#TW9415`cB*AhFpCUufDxk|4Pv4HwW!;P5y227vazVVF^3} QZ2$lO07*qoM6N<$f(=37^8f$< diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 81eb316b13..a777df132f 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -43,7 +43,6 @@ import mekhq.campaign.mission.atb.AtBScenarioFactory; import mekhq.campaign.mission.enums.AtBContractType; import mekhq.campaign.mission.enums.AtBMoraleLevel; -import mekhq.campaign.mission.enums.ContractCommandRights; import mekhq.campaign.personnel.Bloodname; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.backgrounds.BackgroundsController; @@ -1721,7 +1720,7 @@ private void noBatchallOfferedDialog(JPanel panel, String title) { */ public JPanel getContractDifficultyStars(Campaign campaign) { final int ERROR = -99; - int difficulty = Math.min(calculateContractDifficulty(campaign), 8); + int difficulty = calculateContractDifficulty(campaign); // Create a new JFrame JFrame frame = new JFrame(); @@ -1730,27 +1729,31 @@ public JPanel getContractDifficultyStars(Campaign campaign) { // Create a pane with FlowLayout JPanel panel = new JPanel(new FlowLayout()); - // Load and scale the image - ImageIcon imageIcon = new ImageIcon("data/images/universe/factions/logo_star_league_orange.png"); - if (difficulty < 1 && difficulty != ERROR) { - imageIcon = new ImageIcon("data/images/universe/factions/logo_star_league_green.png"); - } else if (difficulty > 0) { - imageIcon = new ImageIcon("data/images/universe/factions/logo_star_league_red.png"); - } - - Image scaledImage = imageIcon.getImage().getScaledInstance(40, 40, Image.SCALE_FAST); - imageIcon = new ImageIcon(scaledImage); + // Load and scale the images + ImageIcon skullFull = new ImageIcon("data/images/misc/challenge_estimate_full.png"); + ImageIcon skullHalf = new ImageIcon("data/images/misc/challenge_estimate_half.png"); int iterations = difficulty; if (difficulty == ERROR) { - iterations = 1; - } else if (difficulty < 1) { - iterations = -difficulty + 1; + iterations = 5; } - for (int i = 0; i < iterations; i++) { - panel.add(new JLabel(imageIcon)); + if (iterations % 2 == 1) { + iterations--; + iterations /= 2; + + for (int i = 0; i < iterations; i++) { + panel.add(new JLabel(skullFull)); + } + + panel.add(new JLabel(skullHalf)); + } else { + iterations /= 2; + + for (int i = 0; i < iterations; i++) { + panel.add(new JLabel(skullFull)); + } } return panel; @@ -1789,8 +1792,8 @@ public int calculateContractDifficulty(Campaign campaign) { case LIAISON -> 0.4; // single allied heavy/assault mek, pure guess for now case HOUSE -> 1; // allies with same (G)BV budget case INTEGRATED -> 2; // allies with twice the player's (G)BV budget - default -> 0; }; + if (allyRatio > 0) { SkillLevel alliedSkill = modifySkillLevelBasedOnFaction(employerCode, allySkill); double allySkillMultiplier = getSkillMultiplier(alliedSkill); @@ -1805,11 +1808,17 @@ public int calculateContractDifficulty(Campaign campaign) { } // Calculate difficulty based on the percentage difference between the two forces. - // If the enemy force exceeds the player force, this will be a positive percentage, otherwise negative. double difference = enemyPower - playerPower; double percentDifference = (difference / playerPower) * 100; - return (int) round(percentDifference / 20); + int mappedValue = (int) Math.ceil(Math.abs(percentDifference) / 20); + if (percentDifference < 0) { + mappedValue = 5 - mappedValue; + } else { + mappedValue = 5 + mappedValue; + } + + return Math.min(Math.max(mappedValue, 1), 10); } /** @@ -1934,7 +1943,7 @@ private static double estimateMekStrength(Campaign campaign, String factionCode, // getMekSummary(int index) is NULL for salvage. int genericBattleValue = unitTable.getMekSummary(i).loadEntity().getGenericBattleValue(); int weight = unitTable.getEntryWeight(i); // NOT 0 for salvage - + totalBattleValue += battleValue * weight; totalGBV += genericBattleValue * weight; rollingCount += weight; From e0ac5089f18a596002a57d7654e59842e3b17692 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 15:21:57 -0500 Subject: [PATCH 066/118] Refactored contract difficulty display and added tooltips Changed difficulty display method from stars to skulls to provide clearer representation of contract difficulty. Added tooltips for contract difficulty and wrapped long text blocks for better readability in ContractMarketDialog. --- .../resources/ContractMarketDialog.properties | 73 +++++++++++-------- .../mekhq/campaign/mission/AtBContract.java | 8 +- .../gui/dialog/ContractMarketDialog.java | 56 +++++++++----- .../mekhq/gui/view/ContractSummaryPanel.java | 6 +- 4 files changed, 86 insertions(+), 57 deletions(-) diff --git a/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties b/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties index 0c9f2132f1..92e7a4d70f 100644 --- a/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties +++ b/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties @@ -16,6 +16,9 @@ btnStartRetainer.text=Accept Retainer Contract lblName.text=Contract Name lblChallenge.text=Challenge Estimate: +lblChallenge.tooltip=This represents an estimate of enemy power compared to your current TO&E.\ +
\ +
Each half-skull is around 20% of your power, so 2.5 skulls means your forces are roughly equal. lblEmployer.text=Employer: lblEnemy.text=Enemy: lblMissionType.text=Mission Type: @@ -36,57 +39,63 @@ lblRequiredLances.text=Required Lances: lblRenegotiate.text=Renegotiate # reports -report.UnableToGMContract=Unable to GM-generate contract from the current location. Increase contract search radius or travel to a border world for better odds of success. +report.UnableToGMContract=Unable to GM-generate contract from the current location. Increase contract\ + \ search radius or travel to a border world for better odds of success. #triggerConfirmationDialog incomingTransmission.title=++INCOMING TRANSMISSION++ responseAccept.text=Accept responseReturn.text=Return -messageChallengeUnknown.text=

Intelligence reports indicate the enemy force is currently unknown.\ -
Sensor data from the Area of Operations is limited, and we are unable to confirm the size, composition, or strength of the opposing forces at this time.\ +messageChallengeUnknown.text=Intelligence reports indicate the enemy force is currently unknown. Sensor\ + \ data from the Area of Operations is limited, and we are unable to confirm the size, composition,\ + \ or strength of the opposing forces at this time.\
\
Despite the uncertainty, we trust in your proven ability to adapt and overcome in dynamic conditions.\ -
The success of this mission could offer significant strategic advantages and may yield considerable rewards upon completion.\ + \ The success of this mission could offer significant strategic advantages and may yield considerable\ + \ rewards upon completion.\
\
If you accept this mission, prepare your unit for all potential scenarios. Expect the unexpected.\ -
Alternatively, you may withdraw and await further opportunities.
-messageChallengeVeryEasy.text=
Our intel indicates that the enemy forces in this mission are well below your unit's capabilities.\ -To be frank, this operation appears to be beneath a commander of your experience and firepower.\ -
We're surprised you've chosen to pursue such a low-priority target, given your past accomplishments.\ + \ Alternatively, you may withdraw and await further opportunities. +messageChallengeVeryEasy.text=Our intel indicates that the enemy forces in this mission are well below\ + \ your unit's capabilities. To be frank, this operation appears to be beneath a commander of your\ + \ experience and firepower. We're surprised you've chosen to pursue such a low-priority target,\ + \ given your past accomplishments.\
\ -
That said, you're free to proceed if you see value in this mission - whether for training purposes or other reasons.\ -
We trust your judgment, though this engagement likely offers minimal challenge or reward.\ +
That said, you're free to proceed if you see value in this mission - whether for training purposes\ + \ or other reasons. We trust your judgment, though this engagement likely offers minimal challenge or reward.\
\
If you intend to deploy, confirm your orders.\ -
Otherwise, you may return to review more fitting opportunities.
-messageChallengeEasy.text=
We have reviewed the details of the mission you're considering.\ -
Intel suggests that the enemy forces in the AO are considerably inferior to your current unit's capabilities.\ -
Frankly, we're uncertain why you'd allocate such substantial resources to what appears to be a low-risk engagement.\ +
Otherwise, you may return to review more fitting opportunities. +messageChallengeEasy.text=We have reviewed the details of the mission you're considering. Intel suggests\ + \ that the enemy forces in the AO are considerably inferior to your current unit's capabilities.\ + \ Frankly, we're uncertain why you'd allocate such substantial resources to what appears to be a\ + \ low-risk engagement.\
\ -
While the mission should pose no serious threat, we trust you have your reasons for pursuing this objective.\ -
Perhaps you're looking to sharpen your team's skills, or there's something here we've overlooked.\ -
Regardless, you can expect minimal resistance, and mission success is all but guaranteed.\ +
While the mission should pose no serious threat, we trust you have your reasons for pursuing\ + \ this objective. Perhaps you're looking to sharpen your team's skills, or there's something here\ + \ we've overlooked. Regardless, you can expect minimal resistance, and mission success is all but\ + \ guaranteed.\
\
If you wish to proceed with this operation, confirm your deployment.\ -
Otherwise, you may choose to reserve your forces for more suitable engagements.
-messageChallengeHard.text=
Our analysis of the upcoming mission indicates that the enemy forces will present a formidable challenge.\ -
They are well-armed, well-coordinated, and capable of inflicting significant damage.\ -
While we believe your unit has the skill and firepower to succeed,\ -
you should be prepared for a tough fight and the possibility of sustaining losses.\ +
Otherwise, you may choose to reserve your forces for more suitable engagements. +messageChallengeHard.text=Our analysis of the upcoming mission indicates that the enemy forces will\ + \ present a formidable challenge. They are well-armed, well-coordinated, and capable of inflicting\ + \ significant damage. While we believe your unit has the skill and firepower to succeed, you should\ + \ be prepared for a tough fight and the possibility of sustaining losses.\
\ -
This mission will test your tactical abilities and resourcefulness.\ -
Victory is achievable, but it won't come without cost.\ -
We trust in your leadership to make the right call.\ +
This mission will test your tactical abilities and resourcefulness. Victory is achievable, but\ + \ it won't come without cost. >We trust in your leadership to make the right call.\
\
If you are ready to face this challenge, confirm your deployment.\ -
Otherwise, you may return to assess alternative missions.
-messageChallengeVeryHard.text=
We've reviewed the mission details, and it's our duty to inform you that the enemy force is well beyond your unit's current strength.\ -
Intelligence reports indicate overwhelming opposition - superior in numbers, firepower, and tactical advantage.\ -
Based on the available data, victory in this engagement seems highly improbable.\ +
Otherwise, you may return to assess alternative missions. +messageChallengeVeryHard.text=We've reviewed the mission details, and it's our duty to inform you that\ + \ the enemy force is well beyond your unit's current strength. Intelligence reports indicate overwhelming\ + \ opposition - superior in numbers, firepower, and tactical advantage. Based on the available data,\ + \ victory in this engagement seems highly improbable.\
\
Given the extreme risk and near-certain loss, we have serious doubts about the likelihood of success.\ -
While we respect your resolve, we strongly urge you to reconsider.\ -
Engaging these forces could result in devastating losses for you and your unit.\ + \ While we respect your resolve, we strongly urge you to reconsider. Engaging these forces could\ + \ result in devastating losses for you and your unit.\
\
If you are committed to this course of action, confirm your deployment.\ -
Otherwise, you may return to review more suitable assignments.
+
Otherwise, you may return to review more suitable assignments. diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index a777df132f..477e1ef2c5 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -1713,12 +1713,12 @@ private void noBatchallOfferedDialog(JPanel panel, String title) { } /** - * This method returns a {@link JPanel} that represents the difficulty stars for a given mission. + * This method returns a {@link JPanel} that represents the difficulty skulls for a given mission. * - * @param campaign the campaign for which the difficulty stars are calculated - * @return a {@link JPanel} with the difficulty stars displayed + * @param campaign the campaign for which the difficulty skulls are calculated + * @return a {@link JPanel} with the difficulty skulls displayed */ - public JPanel getContractDifficultyStars(Campaign campaign) { + public JPanel getContractDifficultySkulls(Campaign campaign) { final int ERROR = -99; int difficulty = calculateContractDifficulty(campaign); diff --git a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java index e538dd321b..bb881e67e1 100644 --- a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java @@ -21,6 +21,7 @@ package mekhq.gui.dialog; import megamek.client.ui.preferences.*; +import megamek.client.ui.swing.util.UIUtil; import megamek.common.util.sorter.NaturalOrderComparator; import megamek.logging.MMLogger; import mekhq.MekHQ; @@ -41,6 +42,8 @@ import javax.swing.table.TableRowSorter; import java.awt.*; import java.awt.event.ActionEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.util.List; import java.util.*; @@ -48,7 +51,7 @@ /** * Presents contract offers generated by ContractMarket - * + *

* Code borrowed heavily from PersonnelMarketDialog * * @author Neoancient @@ -508,21 +511,25 @@ private boolean triggerConfirmationDialog() { ImageIcon icon = getFactionLogo(campaign, ((AtBContract) selectedContract).getEmployerCode(), true); - // Pick an appropriate message - String message = ""; - if (difficulty == -99) { - message = String.format(resourceMap.getString("messageChallengeUnknown.text")); - } else if (difficulty < -4) { - message = String.format(resourceMap.getString("messageChallengeVeryEasy.text")); - } else if (difficulty <= -3) { - message = String.format(resourceMap.getString("messageChallengeEasy.text")); - } else if (difficulty > 4) { - message = String.format(resourceMap.getString("messageChallengeVeryHard.text")); - } else if (difficulty >= 3) { - message = String.format(resourceMap.getString("messageChallengeHard.text")); - } - - // If no message is picked, act as if the player had accepted the mission. + // Define resource keys for each difficulty level + Map difficultyMessages = Map.of( + -99, "messageChallengeUnknown.text", + 1, "messageChallengeVeryEasy.text", + 2, "messageChallengeEasy.text", + 9, "messageChallengeHard.text", + 10, "messageChallengeVeryHard.text" + ); + + // Extract the resource mapping + String resourceKey = difficultyMessages.getOrDefault(difficulty, ""); + String messageResource = resourceMap.getString(resourceKey); + + // Format the HTML message + String message = String.format("

%s
", + UIUtil.scaleForGUI(500), + messageResource); + + // If no message for provided difficulty, act as if the player had accepted the mission. if (message.isBlank()) { return true; } @@ -541,8 +548,8 @@ private boolean triggerConfirmationDialog() { // Create a custom dialog JDialog dialog = new JDialog(); - dialog.setTitle(title); // Set the title of the dialog - dialog.setLayout(new BorderLayout()); // Set a border layout manager + dialog.setTitle(title); + dialog.setLayout(new BorderLayout()); // Create an accept button and add its action listener. // When clicked, it will set the result to true and close the dialog @@ -561,6 +568,15 @@ private boolean triggerConfirmationDialog() { dialog.dispose(); }); + // Attach a window listener + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + result[0] = false; + dialog.dispose(); + } + }); + // Create a panel for buttons and add buttons to it JPanel buttonPanel = new JPanel(); buttonPanel.add(acceptButton); @@ -573,9 +589,9 @@ private boolean triggerConfirmationDialog() { dialog.pack(); // Size the dialog to fit the preferred size and layouts of its components dialog.setLocationRelativeTo(null); // Center the dialog on the screen dialog.setModal(true); // Make the dialog block user input to other top-level windows - dialog.setVisible(true); // Show the dialog + dialog.setVisible(true); - return result[0]; // Return the result when the dialog is disposed + return result[0]; } private void btnCloseActionPerformed(ActionEvent evt) { diff --git a/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java b/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java index 05894e444a..cae55caeba 100644 --- a/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java +++ b/MekHQ/src/mekhq/gui/view/ContractSummaryPanel.java @@ -40,6 +40,8 @@ import java.awt.event.MouseEvent; import java.util.ResourceBundle; +import static megamek.client.ui.WrapLayout.wordWrap; + /** * Contract summary view for ContractMarketDialog * @@ -167,11 +169,13 @@ private void fillStats() { if (campaign.getCampaignOptions().isUseGenericBattleValue()) { if (contract instanceof AtBContract) { JLabel lblChallenge = new JLabel(resourceMap.getString("lblChallenge.text")); + lblChallenge.setToolTipText(wordWrap(resourceMap.getString("lblChallenge.tooltip"))); lblChallenge.setName("lblChallenge"); gridBagConstraintsLabels.gridy = ++y; mainPanel.add(lblChallenge, gridBagConstraintsLabels); - JPanel txtChallenge = ((AtBContract) contract).getContractDifficultyStars(campaign); + JPanel txtChallenge = ((AtBContract) contract).getContractDifficultySkulls(campaign); + txtChallenge.setToolTipText(wordWrap(resourceMap.getString("lblChallenge.tooltip"))); txtChallenge.setName("txtChallenge"); gridBagConstraintsText.gridy = y; mainPanel.add(txtChallenge, gridBagConstraintsText); From 99e8a40c4a330f861a24eb4b7572c116fb60a04d Mon Sep 17 00:00:00 2001 From: Weaver Date: Sat, 19 Oct 2024 13:43:54 -0700 Subject: [PATCH 067/118] Add cost display to mek locations that need repair. Refactor cost functions a little. --- MekHQ/src/mekhq/campaign/Campaign.java | 2 +- .../src/mekhq/campaign/parts/MekLocation.java | 4 ++++ MekHQ/src/mekhq/campaign/parts/Part.java | 20 +++++++++++++++++-- MekHQ/src/mekhq/campaign/parts/PodSpace.java | 11 ++++++++++ MekHQ/src/mekhq/campaign/work/IPartWork.java | 8 ++++++++ .../mekhq/campaign/parts/MekLocationTest.java | 6 +++++- 6 files changed, 47 insertions(+), 4 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index ac638a50d4..f965d8811e 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -3507,7 +3507,7 @@ public String fixPart(IPartWork partWork, Person tech) { if (getCampaignOptions().isPayForRepairs() && action.equals(" fix ") && !(partWork instanceof Armor)) { - Money cost = partWork.getActualValue().multipliedBy(0.2); + Money cost = partWork.getUndamagedValue().multipliedBy(0.2); report += "
Repairs cost " + cost.toAmountAndSymbolString() + " worth of parts."; diff --git a/MekHQ/src/mekhq/campaign/parts/MekLocation.java b/MekHQ/src/mekhq/campaign/parts/MekLocation.java index 122150a7ec..9c4f5e26b6 100644 --- a/MekHQ/src/mekhq/campaign/parts/MekLocation.java +++ b/MekHQ/src/mekhq/campaign/parts/MekLocation.java @@ -604,6 +604,10 @@ public String getDetails(boolean includeRepairDetails) { toReturn.append(" (") .append(Math.round(100 * getPercent())) .append("%)"); + if (campaign.getCampaignOptions().isPayForRepairs()) { + toReturn.append(", ") + .append(getUndamagedValue().multipliedBy(0.2).toAmountAndSymbolString() + " to repair"); + } } } diff --git a/MekHQ/src/mekhq/campaign/parts/Part.java b/MekHQ/src/mekhq/campaign/parts/Part.java index e3bc345598..71a4187e9a 100644 --- a/MekHQ/src/mekhq/campaign/parts/Part.java +++ b/MekHQ/src/mekhq/campaign/parts/Part.java @@ -238,18 +238,34 @@ public Money getActualValue() { return adjustCostsForCampaignOptions(getStickerPrice()); } + @Override + public Money getUndamagedValue() { + return adjustCostsForCampaignOptions(getStickerPrice(), true); + } + @Override public boolean isPriceAdjustedForAmount() { return false; } /** - * Adjusts the cost of a part based on one's campaign options + * Adjusts the cost of a part based on campaign options and the part's condition * * @param cost the part's base cost * @return the part's cost adjusted for campaign options */ public Money adjustCostsForCampaignOptions(@Nullable Money cost) { + return adjustCostsForCampaignOptions(cost, false); + } + + /** + * Adjusts the cost of a part based on campaign options + * + * @param cost the part's base cost + * @param ignoreDamage do we ignore damaged condition + * @return the part's cost adjusted for campaign options + */ + public Money adjustCostsForCampaignOptions(@Nullable Money cost, boolean ignoreDamage) { // if the part doesn't cost anything, no amount of multiplication will change it if ((cost == null) || cost.isZero()) { return Money.zero(); @@ -276,7 +292,7 @@ public Money adjustCostsForCampaignOptions(@Nullable Money cost) { .getUsedPartPriceMultipliers()[getQuality().toNumeric()]); } - if (needsFixing() && !isPriceAdjustedForAmount()) { + if (!ignoreDamage && needsFixing() && !isPriceAdjustedForAmount()) { cost = cost.multipliedBy((getSkillMin() > SkillType.EXP_ELITE) ? campaign.getCampaignOptions().getUnrepairablePartsValueMultiplier() : campaign.getCampaignOptions().getDamagedPartsValueMultiplier()); diff --git a/MekHQ/src/mekhq/campaign/parts/PodSpace.java b/MekHQ/src/mekhq/campaign/parts/PodSpace.java index 7515048bff..91d4cb29bf 100644 --- a/MekHQ/src/mekhq/campaign/parts/PodSpace.java +++ b/MekHQ/src/mekhq/campaign/parts/PodSpace.java @@ -516,6 +516,17 @@ public Money getActualValue() { return Money.of(0.0); } + /** + * This is the value of the part that may be affected by characteristics and campaign options + * but which ignores damage + * (Note: Pod Space, an abstraction, does not have value or price. + * @return the part's actual value + */ + @Override + public Money getUndamagedValue() { + return Money.of(0.0); + } + @Override public boolean isPriceAdjustedForAmount(){ return false; diff --git a/MekHQ/src/mekhq/campaign/work/IPartWork.java b/MekHQ/src/mekhq/campaign/work/IPartWork.java index 18b8c6f1b5..309829e59d 100644 --- a/MekHQ/src/mekhq/campaign/work/IPartWork.java +++ b/MekHQ/src/mekhq/campaign/work/IPartWork.java @@ -138,5 +138,13 @@ static PartRepairType findCorrectRepairType(IPartWork part) { */ public abstract Money getActualValue(); + /** + * This is the value of the part that may be affected by characteristics and campaign options + * but not affected by part damage + * @return the part's actual value if it wasn't damaged + */ + public abstract Money getUndamagedValue(); + + public abstract boolean isPriceAdjustedForAmount(); } diff --git a/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java b/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java index 380ebed1ea..789d875eb6 100644 --- a/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java +++ b/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java @@ -2017,7 +2017,9 @@ void getRepairPartTypeTest() { @Test void getDetailsSpareTest() { Campaign mockCampaign = mock(Campaign.class); - + CampaignOptions mockCampaignOptions = mock(CampaignOptions.class); + when(mockCampaign.getCampaignOptions()).thenReturn(mockCampaignOptions); + MekLocation mekLocation = new MekLocation(Mek.LOC_CT, 25, 0, false, false, false, false, false, mockCampaign); assertNotNull(mekLocation.getDetails()); @@ -2062,6 +2064,8 @@ void getDetailsSpareTest() { @Test void getDetailsOnUnitTest() { Campaign mockCampaign = mock(Campaign.class); + CampaignOptions mockCampaignOptions = mock(CampaignOptions.class); + when(mockCampaign.getCampaignOptions()).thenReturn(mockCampaignOptions); Unit unit = mock(Unit.class); Mek entity = mock(Mek.class); when(unit.getEntity()).thenReturn(entity); From 67fdf7eeed04cfe887e814280039748a597dfff0 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 15:57:46 -0500 Subject: [PATCH 068/118] Replaced grey skulls with black --- .../images/misc/challenge_estimate_full.png | Bin 1838 -> 2142 bytes .../images/misc/challenge_estimate_half.png | Bin 1090 -> 852 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/MekHQ/data/images/misc/challenge_estimate_full.png b/MekHQ/data/images/misc/challenge_estimate_full.png index 76cdc939c46c7a452430fc26a4e48b44fa65352d..e3e34095bf74be68f283beb4d05c29abca196c7e 100644 GIT binary patch literal 2142 zcmV-k2%-0hP)Px#4rN$LW=%~1DgXcg2mk;800000(o>TF00;g_L_t(o3B{RNY?M_L$2-%RPN$u= zlvGfGwk%}{B8z~95|*0agT@6DUrb0aMqhmN#RsB^CdT+=q6QO908PMz2W3e}5C}v_ z89-^Fv9)1e3YM+QblTbK??23^UuW(&of#>d>&^MXVVak&zJ>Ht!K zEXP0<J^lWDZ41gDMu&|Q0{@RQ5WB(e5JU!_>bP+-t+Y5OgRHCEiJ7F=rSxf z!E2aw4$`0VOrr#bJ5E>ajJ6! zIRGDpxAgb-A3zSnj-*eQjlAXM>&s#+& zppi0GF*G!EhtJVUNa)h?xJY*m<7t-xn1TI(J&PQATxglM52qRL6vnic$`ZyEVk}Cy zbRn-ZPU&YBTm_FsG4wjoxr!`B)}c@fr-z=tYzG^^&t%XDn1M=Zg9e3LwEf0}KWlQ* z&lKu94V&R5u<46w;g=X6g!jVjfC-?xkoMI8UyiJYEgFo)Vh6+F@ZVO0U8b)t*@ux5 z4AxVb3v0(}6WnI99rS%ZFfee0v;GE`l)vDkUa$9J%41qUI_L-hzlT5LwQ(H1n*h3s z-YMp2!KNWE&p4inR2Sx@u#NJ+uemIAg=u(IReAHvxrHtHuRhhQT) z0%r6Om+3Z)U8k&1`4m9vkm=V=A74XYS21B1W4<6(TM3;m7QB^qecnyn4VsYl1*@zF z>r7Y+x`FPu=v|@gNXAWPjb>B$S@8?h<$-DdJ;MeYU_;1gyt1;g3b3y+;Rmo5a{!%f z!^6Y!& zi6GwH#i3=qLs*KA)=~ppWCCrD)v$i#6JUW~122ZNem?q*gspCD?T1g})e&SEuzzx3 zXb))5IMP^vO^RNAx_Xh058(x{cDEj^xscM;B|z>aHE!oVaV{4n`tR#=%t2aOh`8{iSZeuI81G776dt4HY{Mfn1D19(1o z6wmqzwS}_6VlLbc>o?$^Xq$xm5viY66-Moh)u;b~l(m(O*~Eeyk!^TcStMt2N-<6b z5;}4LqZ9BV2ZL@__uyYqXn}iZ?*N!ixEQR>bOw)Zr*9W}myz=LJTgcJxjYhSx3lBKxDsA%KfWMLoo+UcdU6 z)-5c*`Lj?hrB6o~?TpK%>yql^*pcZKd zs9{NBj6yaxz+_XpT*^BH}7%(0FeoF U3w%u6)Bpeg07*qoM6N<$f)ttW$N&HU literal 1838 zcmV+}2hsS6P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91D4+uX1ONa40RR91C;$Ke0D9(TtN;K8m`OxIR9FeUm}^X2RS}WJ`sBEnBwi zjf#pojKU){|D`G_a!zpX^z`%>b#-;82L}g-m!iKE18q4uIR~Soqd#K8l7(U_fzkah0E+UzXJ*_c1nDR#ujb{;1g;RtEZ&l$121_^6rk1LFUK zk(p+5`I-%E-@g4%Utiyq@|KBFE+8P_NiABz{) z)2XScj~lIFWS~7UG4UHC;g#e?L`38m&2MC&$eC`v(IgLoXSdr!jpi{ja1KFfs|st^ zu6>sUz3lN~AQ*;V@TyujzgQy3Prt60fhj2|ZzE)te)My!Xy3~~Qj23nSG1m7u0s#- zGO((u>Qf|EJqB4*t~@X2cp0b&PJveyxSRiDK`+O61tmg~Vhqy^leAJgy~p@n$}K6e zcF`>36`eN9tas+-=0*sg&FJk%XC2K0sBX@AFqDqrkFaV*W&xlkuxpuf_}y4-aa1F4Aa= zeXd`>{;F0GFPz7?*WtO1lkxWU_8u3UA3Asvd2#=m$81bY`4Qy*`t7f4t5$D??9TecH z>gw4D{8xnP?CdO3x2dC}L&Cvl+qP}L3DyJEOC-D`nzNyyL8=(jtYK=PC)0Lwm|&va+(8Z{NPXKR7rzjx9QYjO!2Hx^?SIMP9rtys3eKf#byI z=g6Mq&mC08Wj=T83GdFGJExT~LuBPaQP6+T*zw#MjC-Fkw=SnuJmwP~9-gvw>(-M( z*VNQ}1)3HbNK~4KaWcTJd?45i+>4k9gq!d^*hxuA*)aTrf`UTUtXcCKbVz!7dOldb zlP!VpQ2&IXoaGjCI4qUb88>d+c#$Ug&J_}D>6l8ZGG=&ka`G3mG2t6tX{rJSmJ>&r z9F|^a@wVhVGtkDXmiCZmuD@Z(;p-kBAOG&kl`AJa1auwKiF9(VF7fgZ2jvesk{Xr` zLqkKytE;OmV@^NmVln6;-{0Tg+tk#Q2h%Y-JNvW8;6i5xQhU7-W{z5lftPRGxbXvH zmbqhxnd7f}s`g?5(Y>&+~5c6qU;}obn!SkuLx!S}q zSAhYZ#ITEat(FIXCqz9vJ9{4}7#-L3DZB>`VPRo$q!4F8L4nkGKE=hw9||V-MF|EZ zk*m4WwL4sgA`>inGv+oj zT|Z{l$8(LfwYBT0)Nh)}2!HqP-LkG2=MDq6r6K^htmnk}vemQmEc0LF4v-5^DSAeN z6O>{v@FsoUXi&%VFiaiO8>r{I|A{)F$9&mkau|;3`FPDyUtfO!kDG;yUge(qF0bCR c_I)b;5ALN*ifs{6$N&HU07*qoM6N<$f>bkWM*si- diff --git a/MekHQ/data/images/misc/challenge_estimate_half.png b/MekHQ/data/images/misc/challenge_estimate_half.png index 8356fbaf2e84d6181f41788de92c099406fdf7ee..49b49c6113778fad217b2c7a97a19602e5d18b66 100644 GIT binary patch delta 816 zcmV-01JC@z2-F6UF@FwaSV?A0O#mtY000O800000007cclK=n$^hrcPR7ee-mOE$^ zVGze}cQ1SIgPfvCKoRj3K_Q4>p@@yxSy%+bM$%~^YM07VEW|Qac4}i|A-*cBpkSp% zL`3irL^N;@?!D)4F1yFxX7+a1f$!#<+5h}^=9}-nZ;jOdiGNAhcH6dhGUg4#xQIQ= z$QTLu?0i1IWLeftVsN%Bljf5M)1^{r zjo`hL2!hXKaaAc6i?4Z*h)jr7LOQod=T56{ig0Qro0M85t`HHcK2|dXX{qQKPL0r^ zt4@qq6`~OqS%1HrijLvjLYU2FuM?-OZ45f4%cg1eV?~C5v`lhzxek6y_8}Z&ZY`)u zQ(9xF1cpXv(Uh=l^QG78Eix|fjwg&k@LgKTDN23=@RT1+)+_aT{VG2HmYN0o0bj+# zs)U(L<^X>`IT$>7TC3I0dqzN)A^n3dEq<-!RJ+~&#DBAo0&u10O%z?mHzY$>RYD1B zwOUJ}ql8oZtcz(;#hy^YZnygu;13tPA8osHzcSdjm&HM&&Y(6z33(_T{KgPD!aDs# z?;Bd1KXcL$cEm{<_Nv)zu8P~M-G>{S`kS^5g0+6Xe+9%2E~{JUVM3iw=e`?dM-et% zpr=fTR)0Lh(-6LN=izH80LtljL6bB$;xV32A$fmEY7DpDan?v06eY6e#-yeU398lV zJ2v@YjPDVQ!{j0}43Nywm>2kt2K*y4GQ$yabMlMLdxB}7Wq1^LHZ&6vlHCnHOBNi* zdCWl}CwmHFxxc^C>2v7ci3rK=0iB<2G#Zz0000mT#nrq-fFOvQq z7^Rw!pb3z`!te?538vQ46k;J@f!fw{OW$LBZ~97^WP0!1d(U}yX70>X(Cn{iD=RDQ zfL6L(uBM`*qKnWS=mkO^e8$$+)?TaCdIy+VbOR5&7eb+>rR8Q>S=r-kBrI_*LZi>; z8`kUf9u}mgtA7#dySln&06wHfAopqI265cc(eVj-USx#j5;iwC_ZW@FGa`9Dl1Hd@ zI-Sq*Az@>Agyh$;LHX!B!XyZ&@2e>Y%gf6xU^4BC4Il+!eSQ5c*wxBO^;-%;olcif zMdWbuGBKf`*MU=fEC*NG%>%{Y+1v}57sDf@lHrz3Du0z)2#3Qx=ob+48a*W^)yoYS zCz8qJs8*{z&HA>txBCYN2d~2>meiZNx;jT=W8(yDWnwIE4K-C&RY%-zH(dafSSAl(%~xPcC&!zzCKwEU2gxJ@{LAS7skkQ(h-8p!TtY(3&d&Cd%++v_bY)^T zM@B~KvwspUE-sED%OC{{f^ZnZm;tgvK;2#;Lm&`1ySlpiJ1Ze#z?TYS(8sV&zi=#F znX$%hw>vvKJ0}bVLwh2T@J~-q50j6#%OZv(yJt_w5Pfkt_kSs1P&c22efPYrJBh-JZtE;_;FccORn%OBSP*|g{ z*pO2S2j4)aM&KSXF&<=o)L9cYCk&m|i)t=gODCtAp&W@_K8TI^2>WrlPaLb2jTj@z zNF)-TnwmNZLn0oJzmOb^k{4ljclT|F!{Ld=VlPqnbhB$fOz-#N!otEi$VgszN{ zDr|JJ*b7C%WmPN|O9hT#s=dAa2?hClzK0}BN=mFG(X^^%vlFxXi*swKt*yNadBSG1 z{WP1+k1&p-znh<*?`vyodmxn!zI*Cc6fXnKhdw?_nvJB#9_3xStG8j0H`G%zqwv$eJLUZP?Y zY04QJjRmjQtHadeOdMB5+zrgCk1%3@q=<%$6zz& Date: Sat, 19 Oct 2024 16:05:13 -0500 Subject: [PATCH 069/118] Correct tooltip text in ContractMarketDialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the tooltip text incorrectly implied possession by the user. The updated text clarifies that the estimation refers to the forces’ power equivalence in a general context. --- MekHQ/resources/mekhq/resources/ContractMarketDialog.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties b/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties index 92e7a4d70f..e31f2f2500 100644 --- a/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties +++ b/MekHQ/resources/mekhq/resources/ContractMarketDialog.properties @@ -18,7 +18,7 @@ lblName.text=Contract Name lblChallenge.text=Challenge Estimate: lblChallenge.tooltip=This represents an estimate of enemy power compared to your current TO&E.\
\ -
Each half-skull is around 20% of your power, so 2.5 skulls means your forces are roughly equal. +
Each half-skull is around 20% of your power, so 2.5 skulls means the forces are roughly equal. lblEmployer.text=Employer: lblEnemy.text=Enemy: lblMissionType.text=Mission Type: From c6c33f65fc87d1ac96e68f0090a50722ef7c2de0 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 16:15:16 -0500 Subject: [PATCH 070/118] Simplify resource key handling Added a check to return true if the resource key is not found in the mapping, effectively assuming the player accepts the mission by default. This change ensures smoother handling of undefined difficulty levels and avoids potential errors. --- MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java index bb881e67e1..7efcdb7864 100644 --- a/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ContractMarketDialog.java @@ -520,8 +520,14 @@ private boolean triggerConfirmationDialog() { 10, "messageChallengeVeryHard.text" ); - // Extract the resource mapping + // Extract the resource mapping and get the resource string String resourceKey = difficultyMessages.getOrDefault(difficulty, ""); + + // If resourceKey is not found, just return true, acting as if the player had accepted the mission + if (resourceKey.isEmpty()) { + return true; + } + String messageResource = resourceMap.getString(resourceKey); // Format the HTML message From 52bc6e7833bccd28dd5539d64b0e84611c7010cb Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 17:42:03 -0500 Subject: [PATCH 071/118] Updated Primary Allies Scenario Modifiers Updated briefing text for clarity and adjusted flags, so allied units now contribute to BV and unit count. --- MekHQ/data/scenariomodifiers/PrimaryAlliesAir.xml | 8 ++++---- MekHQ/data/scenariomodifiers/PrimaryAlliesGround.xml | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/MekHQ/data/scenariomodifiers/PrimaryAlliesAir.xml b/MekHQ/data/scenariomodifiers/PrimaryAlliesAir.xml index ebffd711bc..5047e32dfe 100644 --- a/MekHQ/data/scenariomodifiers/PrimaryAlliesAir.xml +++ b/MekHQ/data/scenariomodifiers/PrimaryAlliesAir.xml @@ -1,5 +1,5 @@ - To compensate for the presence of a large opposing force, allied units will be working alongside us on this operation. + Additional allied units will be working alongside us on this operation. true PreForceGeneration @@ -8,16 +8,16 @@ -3 0 false - false + true true - false + true false 5 -1 1 1.0 - Allied Force + Additional Force 3 1 4 diff --git a/MekHQ/data/scenariomodifiers/PrimaryAlliesGround.xml b/MekHQ/data/scenariomodifiers/PrimaryAlliesGround.xml index 893eb6e609..99f4f27b6c 100644 --- a/MekHQ/data/scenariomodifiers/PrimaryAlliesGround.xml +++ b/MekHQ/data/scenariomodifiers/PrimaryAlliesGround.xml @@ -1,5 +1,5 @@ - To compensate for the presence of a large opposing force, allied units will be working alongside us on this operation. + Additional allied units will be working alongside us on this operation. AllGroundTerrain SpecificGroundTerrain @@ -12,16 +12,16 @@ -2 0 false - false + true true - false + true false 5 -1 1 1.0 - Allied Force + Additional Force 3 1 4 From f4e64989fa4e8653b9c8ef1e006a9e5fc5ce2a3a Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 18:17:51 -0500 Subject: [PATCH 072/118] Fixed Wrapping of Personality Description Removed the markdown rendering of the personality description and the word wrapping from the text field. As well as two incorrect uses of `wordWrap`. --- MekHQ/src/mekhq/gui/view/PersonViewPanel.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java index afa2b71c4a..f42cd6cb92 100644 --- a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java @@ -88,7 +88,6 @@ public PersonViewPanel(Person p, Campaign c, CampaignGUI gui) { } private void initComponents() { - setLayout(new GridBagLayout()); getAccessibleContext().setAccessibleName("Details for " + person.getFullName()); @@ -204,7 +203,7 @@ private void initComponents() { txtDesc.setName("personalityDescription"); txtDesc.setEditable(false); txtDesc.setContentType("text/html"); - txtDesc.setText(MarkdownRenderer.getRenderedHtml(wordWrap(person.getPersonalityDescription(), 200))); + txtDesc.setText(person.getPersonalityDescription()); txtDesc.setBorder(BorderFactory.createTitledBorder(resourceMap.getString("pnlPersonality.title"))); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; @@ -222,7 +221,7 @@ private void initComponents() { txtDesc.setName("txtDesc"); txtDesc.setEditable(false); txtDesc.setContentType("text/html"); - txtDesc.setText(MarkdownRenderer.getRenderedHtml(wordWrap(person.getBiography(), 200))); + txtDesc.setText(MarkdownRenderer.getRenderedHtml(person.getBiography())); txtDesc.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createTitledBorder(resourceMap.getString("pnlDescription.title")), BorderFactory.createEmptyBorder(0, 2, 2, 2))); From 42c04e78d45eb799917218b62fdc1f95e1950abe Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 18:24:14 -0500 Subject: [PATCH 073/118] Updated Fatigue Column of Personnel Table to Use Effective Fatigue Value Modified the `PersonnelTableModelColumn` to use the effective fatigue value for personnel instead of the raw fatigue. --- .../mekhq/gui/enums/PersonnelTableModelColumn.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java b/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java index f78ed7cd73..77bf995d8f 100644 --- a/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java +++ b/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java @@ -490,11 +490,8 @@ public String getCellValue(final Campaign campaign, final PersonnelMarket person case GENDER: return GenderDescriptors.MALE_FEMALE_OTHER.getDescriptorCapitalized(person.getGender()); case SKILL_LEVEL: - StringBuilder levelName = new StringBuilder(64); - levelName.append("") - .append(SkillType.getColoredExperienceLevelName(person.getSkillLevel(campaign, false))) - .append(""); - return levelName.toString(); + return "" + SkillType.getColoredExperienceLevelName( + person.getSkillLevel(campaign, false)) + ""; case PERSONNEL_ROLE: return person.getRoleDesc(); case UNIT_ASSIGNMENT: { @@ -673,7 +670,7 @@ public String getCellValue(final Campaign campaign, final PersonnelMarket person case INJURIES: if (campaign.getCampaignOptions().isUseAdvancedMedical()) { return Integer.toString(person.getInjuries().size()); - } else { + } else { return Integer.toString(person.getHits()); } case KILLS: @@ -720,7 +717,7 @@ public String getCellValue(final Campaign campaign, final PersonnelMarket person case TOUGHNESS: return Integer.toString(person.getToughness()); case FATIGUE: - return Integer.toString(person.getFatigue()); + return Integer.toString(person.getEffectiveFatigue(campaign)); case EDGE: return Integer.toString(person.getEdge()); case SPA_COUNT: From 4c192fae27adfa5802cea08e2066ef2a40b1a560 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 18:57:54 -0500 Subject: [PATCH 074/118] Fixed New Day Reporting Corrected whitespace and formatting inconsistencies within various methods in the `Campaign` class. Added triggering of `NewDayEvent` at the start of each new day cycle, instead of the end. --- .../mekhq/resources/Campaign.properties | 4 ++-- MekHQ/src/mekhq/campaign/Campaign.java | 23 +++++++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/MekHQ/resources/mekhq/resources/Campaign.properties b/MekHQ/resources/mekhq/resources/Campaign.properties index aba27e1309..85bf3cc4f7 100644 --- a/MekHQ/resources/mekhq/resources/Campaign.properties +++ b/MekHQ/resources/mekhq/resources/Campaign.properties @@ -39,8 +39,8 @@ LayeredForceIconLayer.LOGO.text=Logos LayeredForceIconLayer.LOGO.toolTipText=This tab contains canon faction logos that can be added to the center of a force icon. #### Anniversaries -anniversaryBirthday.text=%s is %s%s%s today! -anniversaryRecruitment.text=%s celebrates %s%s%s years with %s! +anniversaryBirthday.text=%s is %s%s%s today! +anniversaryRecruitment.text=%s celebrates %s%s%s years with %s! #### Personnel Removal personnelRemoval.text=Old personnel records have been tidied away. diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index ac638a50d4..b81b6b83fc 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -2250,13 +2250,11 @@ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { if (ignoreMothballedUnits && (null != incomingPart.getUnit()) && incomingPart.getUnit().isMothballed()) { - return; } else if ((incomingPart.getUnit() != null) || (incomingPart instanceof MissingPart)) { partInUse.setUseCount(partInUse.getUseCount() + getQuantity(incomingPart)); } else { if (incomingPart.isPresent()) { if (incomingPart.getQuality().toNumeric() < ignoreSparesUnderQuality.toNumeric()) { - return; } else { partInUse.setStoreCount(partInUse.getStoreCount() + getQuantity(incomingPart)); partInUse.addSpare(incomingPart); @@ -2273,7 +2271,7 @@ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, * @param ignoreMothballedUnits don't count parts in mothballed units * @param ignoreSparesUnderQuality don't count spare parts lower than this quality */ - public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, + public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { partInUse.setUseCount(0); partInUse.setStoreCount(0); @@ -2290,7 +2288,7 @@ public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartInUse newPiu = getPartInUse((Part) maybePart); if (partInUse.equals(newPiu)) { partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? + + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() : (Part) maybePart) * maybePart.getQuantity()); } @@ -2334,7 +2332,7 @@ public Set getPartsInUse(boolean ignoreMothballedUnits, inUse.put(partInUse, partInUse); } partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? + + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() : (Part) maybePart) * maybePart.getQuantity()); @@ -3770,7 +3768,8 @@ private void processNewDayATB() { AtBMoraleLevel morale = contract.getMoraleLevel(); - String report = "Current enemy condition is '" + morale + "' on contract " + contract.getName() + "

" + morale.getToolTipText() + ""; + String report = "Current enemy condition is " + morale + " on contract " + + contract.getName() + "

" + morale.getToolTipText(); addReport(report); } @@ -4222,8 +4221,7 @@ public boolean newDay() { setLocalDate(getLocalDate().plusDays(1)); // Determine if we have an active contract or not, as this can get used - // elsewhere before - // we actually hit the AtB new day (e.g., personnel market) + // elsewhere before we actually hit the AtB new day (e.g., personnel market) if (getCampaignOptions().isUseAtB()) { setHasActiveContract(); } @@ -4234,6 +4232,8 @@ public boolean newDay() { newReports.clear(); beginReport("" + MekHQ.getMHQOptions().getLongDisplayFormattedDate(getLocalDate()) + ""); + MekHQ.triggerEvent(new NewDayEvent(this)); + // New Year Changes if (getLocalDate().getDayOfYear() == 1) { // News is reloaded @@ -4297,7 +4297,10 @@ public boolean newDay() { processPersonnelRemoval(); } - MekHQ.triggerEvent(new NewDayEvent(this)); + if ((campaignOptions.isEnableAutoAwards()) && (getLocalDate().getDayOfMonth() == 1)) { + AutoAwardsController autoAwardsController = new AutoAwardsController(); + autoAwardsController.ManualController(this, false); + } // this duplicates any turnover information so that it is still available on the // new day. @@ -8002,7 +8005,7 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT // } break; } - if (p.getQuality().toNumeric() > oldQuality.toNumeric()) { + if (p.getQuality().toNumeric() > oldQuality.toNumeric()) { partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor( MekHQ.getMHQOptions().getFontColorPositiveHexColor(), "new quality is " + p.getQualityName()); From c91137ed24dd1da8cf0a2e9bc8c96464c4d3adce Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 18:58:23 -0500 Subject: [PATCH 075/118] Fix gender assignment in newPerson method The gender parameter was passed incorrectly, causing issues with randomization. Changed the Gender.RANDOMIZE to use the variable 'gender' for proper assignment. --- MekHQ/src/mekhq/campaign/Campaign.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index b81b6b83fc..977750ad36 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -1463,7 +1463,7 @@ public Person newDependent(boolean baby, Gender gender) { person = newPerson(PersonnelRole.DEPENDENT, PersonnelRole.NONE, new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions()), new DefaultPlanetSelector(getCampaignOptions().getRandomOriginOptions()), - Gender.RANDOMIZE); + gender); } else { person = newPerson(PersonnelRole.DEPENDENT); } From cd4b3f6ecb6134f385f85102ebd8e960c29564e8 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sat, 19 Oct 2024 19:13:16 -0500 Subject: [PATCH 076/118] Re-Added Compatibility Handlers for SPA Skill Prerequisites Updated skill name replacements for backward compatibility with versions <50.01. --- .../mekhq/campaign/personnel/SkillPerquisite.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MekHQ/src/mekhq/campaign/personnel/SkillPerquisite.java b/MekHQ/src/mekhq/campaign/personnel/SkillPerquisite.java index 22daf67444..8cfd4d8698 100644 --- a/MekHQ/src/mekhq/campaign/personnel/SkillPerquisite.java +++ b/MekHQ/src/mekhq/campaign/personnel/SkillPerquisite.java @@ -200,6 +200,18 @@ public static SkillPerquisite generateInstanceFromXML(Node wn) { Node wn2 = nl.item(x); if (wn2.getNodeName().equalsIgnoreCase("skill")) { String skillName = wn2.getTextContent(); + + // <50.01 compatibility handlers + skillName = skillName.replaceAll("Piloting/Mech::", "Piloting/Mek::"); + skillName = skillName.replaceAll("Gunnery/Mech::", "Gunnery/Mek::"); + skillName = skillName.replaceAll("Gunnery/Battlesuit::", "Gunnery/BattleArmor::"); + skillName = skillName.replaceAll("Gunnery/ProtoMech::", "Gunnery/ProtoMek::"); + skillName = skillName.replaceAll("Anti-Mech::", "Anti-Mek::"); + skillName = skillName.replaceAll("Tech/Mech::", "Tech/Mek::"); + skillName = skillName.replaceAll("Tech/BA::", "Tech/BattleArmor::"); + skillName = skillName.replaceAll("Medtech::", "MedTech::"); + // end compatibility handlers + int level = 0; if (skillName.contains("::")) { level = parseStringForLevel(skillName); From 77aa0743047711c219e91b0cc78ab4f726d68c00 Mon Sep 17 00:00:00 2001 From: Weaver Date: Sun, 20 Oct 2024 10:20:20 -0700 Subject: [PATCH 077/118] Fix a display error --- MekHQ/src/mekhq/campaign/parts/MekLocation.java | 6 +++--- MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/MekLocation.java b/MekHQ/src/mekhq/campaign/parts/MekLocation.java index 9c4f5e26b6..44c5e43dce 100644 --- a/MekHQ/src/mekhq/campaign/parts/MekLocation.java +++ b/MekHQ/src/mekhq/campaign/parts/MekLocation.java @@ -872,9 +872,9 @@ public String getDesc() { toReturn.append("") .append(isBlownOff() ? "Re-attach " : "Seal ") .append(getName()) - .append(", ") - .append(getTonnage()) - .append("ton - ") + .append(" (") + .append(getUnitTonnage()) + .append(" ton) - ") .append(ReportingUtilities.messageSurroundedBySpanWithColor( SkillType.getExperienceLevelColor(getSkillMin()), SkillType.getExperienceLevelName(getSkillMin()) + "+")) diff --git a/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java b/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java index 789d875eb6..9bec8cd74d 100644 --- a/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java +++ b/MekHQ/unittests/mekhq/campaign/parts/MekLocationTest.java @@ -2038,7 +2038,6 @@ void getDetailsSpareTest() { assertNotNull(mekLocation.getDetails()); assertTrue(mekLocation.getDetails().startsWith("25 tons")); - //assertTrue(mekLocation.getDetails().contains("(100%)")); assertNotNull(mekLocation.getDetails(false)); assertEquals("25 tons", mekLocation.getDetails(false)); @@ -2046,7 +2045,6 @@ void getDetailsSpareTest() { assertNotNull(mekLocation.getDetails()); assertTrue(mekLocation.getDetails().startsWith("25 tons")); - //assertTrue(mekLocation.getDetails().contains("(100%)")); assertTrue(mekLocation.getDetails().contains("[Sensors]")); assertNotNull(mekLocation.getDetails(false)); assertEquals("25 tons [Sensors]", mekLocation.getDetails(false)); @@ -2055,7 +2053,6 @@ void getDetailsSpareTest() { assertNotNull(mekLocation.getDetails()); assertTrue(mekLocation.getDetails().startsWith("25 tons")); - //assertTrue(mekLocation.getDetails().contains("(100%)")); assertTrue(mekLocation.getDetails().contains("[Sensors, Life Support]")); assertNotNull(mekLocation.getDetails(false)); assertEquals("25 tons [Sensors, Life Support]", mekLocation.getDetails(false)); From edc7f11d8fa7b0f0b1c53e7b41683bd002f46379 Mon Sep 17 00:00:00 2001 From: Weaver Date: Sun, 20 Oct 2024 10:54:05 -0700 Subject: [PATCH 078/118] ForceRenderer - bold unit commanders --- MekHQ/src/mekhq/gui/ForceRenderer.java | 46 +++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/MekHQ/src/mekhq/gui/ForceRenderer.java b/MekHQ/src/mekhq/gui/ForceRenderer.java index a8b7f55cfe..a1ddb73f6b 100644 --- a/MekHQ/src/mekhq/gui/ForceRenderer.java +++ b/MekHQ/src/mekhq/gui/ForceRenderer.java @@ -57,31 +57,29 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean } String c3network = ""; StringBuilder transport = new StringBuilder(); - Unit u = (Unit) value; - Person person = u.getCommander(); + Unit unit = (Unit) value; + Person person = unit.getCommander(); if (person != null) { name = person.getFullTitle(); - name += " (" + u.getEntity().getCrew().getGunnery() + '/' - + u.getEntity().getCrew().getPiloting() + ')'; - if (person.needsFixing() || (u.getEntity().getCrew().getHits() > 0)) { + name += " (" + unit.getEntity().getCrew().getGunnery() + '/' + + unit.getEntity().getCrew().getPiloting() + ')'; + if (person.needsFixing() || (unit.getEntity().getCrew().getHits() > 0)) { name = "" + name + ""; } } - String uname = "" + u.getName() + ""; - if (u.isDamaged()) { + String uname = "" + unit.getName() + ""; + if (unit.isDamaged()) { uname = "" + uname + ""; } - Entity entity = u.getEntity(); + Entity entity = unit.getEntity(); if (entity.hasNavalC3()) { if (entity.calculateFreeC3Nodes() >= 5) { c3network += Messages.getString("ChatLounge.NC3None"); } else { - c3network += Messages - .getString("ChatLounge.NC3Network") - + entity.getC3NetId(); + c3network += Messages.getString("ChatLounge.NC3Network") + entity.getC3NetId(); if (entity.calculateFreeC3Nodes() > 0) { c3network += Messages.getString("ChatLounge.NC3Nodes", entity.calculateFreeC3Nodes()); @@ -91,8 +89,7 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean if (entity.calculateFreeC3Nodes() >= 5) { c3network += Messages.getString("ChatLounge.C3iNone"); } else { - c3network += Messages.getString("ChatLounge.C3iNetwork") - + entity.getC3NetId(); + c3network += Messages.getString("ChatLounge.C3iNetwork") + entity.getC3NetId(); if (entity.calculateFreeC3Nodes() > 0) { c3network += Messages.getString("ChatLounge.C3iNodes", entity.calculateFreeC3Nodes()); @@ -101,16 +98,14 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean } else if (entity.hasC3()) { if (entity.C3MasterIs(entity)) { c3network += Messages.getString("ChatLounge.C3Master"); - c3network += Messages.getString("ChatLounge.C3MNodes", - entity.calculateFreeC3MNodes()); + c3network += Messages.getString("ChatLounge.C3MNodes", entity.calculateFreeC3MNodes()); if (entity.hasC3MM()) { c3network += Messages.getString("ChatLounge.C3SNodes", entity.calculateFreeC3Nodes()); } } else if (!entity.hasC3S()) { c3network += Messages.getString("ChatLounge.C3Master"); - c3network += Messages.getString("ChatLounge.C3SNodes", - entity.calculateFreeC3Nodes()); + c3network += Messages.getString("ChatLounge.C3SNodes", entity.calculateFreeC3Nodes()); // an independent master might also be a slave to a company master if (entity.getC3Master() != null) { c3network += "
" + Messages.getString("ChatLounge.C3Slave") @@ -128,22 +123,27 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean c3network = "
" + c3network + ""; } - if (u.hasTransportShipAssignment()) { + if (unit.hasTransportShipAssignment()) { transport.append("
Transported by: ") - .append(u.getTransportShipAssignment().getTransportShip().getName()); + .append(unit.getTransportShipAssignment().getTransportShip().getName()); } String text = name + ", " + uname + c3network + transport; + + Force force = unit.getCampaign().getForce(unit.getForceId()); + if((null != person) && (null != force) && (person.getId() == force.getForceCommanderID())) { + text = "" + text + ""; + } setText("" + text + ""); - getAccessibleContext().setAccessibleName((u.isDeployed() ? "Deployed Unit: " : "Unit: ") + text); - if (!sel && u.isDeployed()) { + getAccessibleContext().setAccessibleName((unit.isDeployed() ? "Deployed Unit: " : "Unit: ") + text); + if (!sel && unit.isDeployed()) { setForeground(MekHQ.getMHQOptions().getDeployedForeground()); setBackground(MekHQ.getMHQOptions().getDeployedBackground()); setOpaque(true); } } else if (value instanceof Force) { Force force = (Force) value; - getAccessibleContext() - .setAccessibleName((force.isDeployed() ? "Deployed Force: " : "Force: ") + force.getFullName()); + getAccessibleContext().setAccessibleName(( + force.isDeployed() ? "Deployed Force: " : "Force: ") + force.getFullName()); if (!sel && force.isDeployed()) { setForeground(MekHQ.getMHQOptions().getDeployedForeground()); setBackground(MekHQ.getMHQOptions().getDeployedBackground()); From b94733646ecd6cc143293d4e33199dbae02a330c Mon Sep 17 00:00:00 2001 From: Weaver Date: Sun, 20 Oct 2024 13:12:43 -0700 Subject: [PATCH 079/118] Force.setScenarioId now affects its units, redundant code elsewhere removed --- MekHQ/src/mekhq/campaign/Campaign.java | 20 +++---------------- MekHQ/src/mekhq/campaign/force/Force.java | 14 +++++++++---- .../storypoint/ScenarioStoryPoint.java | 8 +------- .../stratcon/StratconRulesManager.java | 2 +- .../campaign/stratcon/StratconScenario.java | 5 +++-- MekHQ/src/mekhq/gui/CampaignGUI.java | 2 +- .../mekhq/gui/adapter/TOEMouseAdapter.java | 8 +------- .../dialog/ForceTemplateAssignmentDialog.java | 2 +- .../gui/stratcon/StratconScenarioWizard.java | 2 +- 9 files changed, 22 insertions(+), 41 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index ac638a50d4..eccafcfa45 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -896,7 +896,7 @@ public void addForce(Force force, Force superForce) { int id = lastForceId + 1; force.setId(id); superForce.addSubForce(force, true); - force.setScenarioId(superForce.getScenarioId()); + force.setScenarioId(superForce.getScenarioId(), this); forceIds.put(id, force); lastForceId = id; @@ -917,15 +917,7 @@ public void moveForce(Force force, Force superForce) { } superForce.addSubForce(force, true); - force.setScenarioId(superForce.getScenarioId()); - - for (Object o : force.getAllChildren(this)) { - if (o instanceof Unit) { - ((Unit) o).setScenarioId(superForce.getScenarioId()); - } else if (o instanceof Force) { - ((Force) o).setScenarioId(superForce.getScenarioId()); - } - } + force.setScenarioId(superForce.getScenarioId(), this); // repopulate formation levels across the TO&E Force.populateFormationLevelsFromOrigin(this); @@ -3697,14 +3689,8 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem } if (!forceUnderRepair) { - forceIds.get(forceId).setScenarioId(s.getId()); + forceIds.get(forceId).setScenarioId(s.getId(), this); s.addForces(forceId); - for (UUID uid : forceIds.get(forceId).getAllUnits(true)) { - Unit u = getHangar().getUnit(uid); - if (u != null) { - u.setScenarioId(s.getId()); - } - } addReport(MessageFormat.format( resources.getString("atbScenarioTodayWithForce.format"), diff --git a/MekHQ/src/mekhq/campaign/force/Force.java b/MekHQ/src/mekhq/campaign/force/Force.java index 008822a834..6fb6ce545a 100644 --- a/MekHQ/src/mekhq/campaign/force/Force.java +++ b/MekHQ/src/mekhq/campaign/force/Force.java @@ -183,10 +183,16 @@ public int getScenarioId() { return scenarioId; } - public void setScenarioId(int i) { - this.scenarioId = i; + public void setScenarioId(int scenarioId, Campaign campaign) { + this.scenarioId = scenarioId; for (Force sub : getSubForces()) { - sub.setScenarioId(i); + sub.setScenarioId(scenarioId, campaign); + } + for (UUID uid : getUnits()) { + Unit unit = campaign.getUnit(uid); + if (null != unit) { + unit.setScenarioId(scenarioId); + } } } @@ -410,7 +416,7 @@ public void clearScenarioIds(Campaign c, boolean killSub) { c.getScenario(getScenarioId()).addUnit(uid); } } - setScenarioId(-1); + setScenarioId(-1,c); } public int getId() { diff --git a/MekHQ/src/mekhq/campaign/storyarc/storypoint/ScenarioStoryPoint.java b/MekHQ/src/mekhq/campaign/storyarc/storypoint/ScenarioStoryPoint.java index 947dcd0066..bf7d6b11f6 100644 --- a/MekHQ/src/mekhq/campaign/storyarc/storypoint/ScenarioStoryPoint.java +++ b/MekHQ/src/mekhq/campaign/storyarc/storypoint/ScenarioStoryPoint.java @@ -95,13 +95,7 @@ public void start() { Force force = getCampaign().getForce(deployedForceId); if (null != force) { scenario.addForces(force.getId()); - force.setScenarioId(scenario.getId()); - for (UUID uid : force.getAllUnits(true)) { - Unit u = getCampaign().getUnit(uid); - if (null != u) { - u.setScenarioId(scenario.getId()); - } - } + force.setScenarioId(scenario.getId(), getCampaign()); } } } diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java index a1b6ff1c53..28dce4c76b 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java @@ -723,7 +723,7 @@ public static void commitPrimaryForces(Campaign campaign, StratconScenario scena for (int forceID : scenario.getPlayerTemplateForceIDs()) { Force force = campaign.getForce(forceID); force.clearScenarioIds(campaign, true); - force.setScenarioId(scenario.getBackingScenarioID()); + force.setScenarioId(scenario.getBackingScenarioID(), campaign); } scenario.commitPrimaryForces(); diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconScenario.java b/MekHQ/src/mekhq/campaign/stratcon/StratconScenario.java index 2ddc5c7ae4..6ae8c6135a 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconScenario.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconScenario.java @@ -17,6 +17,7 @@ import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import mekhq.MekHQ; import mekhq.adapter.DateAdapter; +import mekhq.campaign.Campaign; import mekhq.campaign.event.DeploymentChangedEvent; import mekhq.campaign.force.Force; import mekhq.campaign.mission.AtBDynamicScenario; @@ -91,10 +92,10 @@ public void addPrimaryForce(int forceID) { * Add a force to the backing scenario, trying to associate it with the given template. * Does some scenario and force house-keeping, fires a deployment changed event. */ - public void addForce(Force force, String templateID) { + public void addForce(Force force, String templateID, Campaign campaign) { if (!getBackingScenario().getForceIDs().contains(force.getId())) { backingScenario.addForce(force.getId(), templateID); - force.setScenarioId(getBackingScenarioID()); + force.setScenarioId(getBackingScenarioID(), campaign); MekHQ.triggerEvent(new DeploymentChangedEvent(force, getBackingScenario())); } } diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index 3f4abf8356..4c4febd789 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -2419,7 +2419,7 @@ public void undeployForce(Force f, boolean killSubs) { continue; } scenario.addForces(sub.getId()); - sub.setScenarioId(scenario.getId()); + sub.setScenarioId(scenario.getId(), getCampaign()); } prevId = parent.getId(); } diff --git a/MekHQ/src/mekhq/gui/adapter/TOEMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/TOEMouseAdapter.java index a27365809e..fae28bb097 100644 --- a/MekHQ/src/mekhq/gui/adapter/TOEMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/TOEMouseAdapter.java @@ -361,13 +361,7 @@ public void actionPerformed(ActionEvent action) { force.clearScenarioIds(gui.getCampaign(), true); if (null != scenario) { scenario.addForces(force.getId()); - force.setScenarioId(scenario.getId()); - for (UUID uid : force.getAllUnits(true)) { - Unit u = gui.getCampaign().getUnit(uid); - if (null != u) { - u.setScenarioId(scenario.getId()); - } - } + force.setScenarioId(scenario.getId(), gui.getCampaign()); } MekHQ.triggerEvent(new DeploymentChangedEvent(force, scenario)); } diff --git a/MekHQ/src/mekhq/gui/dialog/ForceTemplateAssignmentDialog.java b/MekHQ/src/mekhq/gui/dialog/ForceTemplateAssignmentDialog.java index b58d2e4fd8..49c4db64e6 100644 --- a/MekHQ/src/mekhq/gui/dialog/ForceTemplateAssignmentDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/ForceTemplateAssignmentDialog.java @@ -208,7 +208,7 @@ private void assignForceToTemplate() { // all this stuff apparently needs to happen when assigning a force to a scenario campaignGUI.undeployForce(force); force.clearScenarioIds(campaignGUI.getCampaign(), true); - force.setScenarioId(currentScenario.getId()); + force.setScenarioId(currentScenario.getId(),campaignGUI.getCampaign()); currentScenario.addForce(forceID, templateList.getSelectedValue().getForceName()); for (UUID uid : force.getAllUnits(true)) { Unit u = campaignGUI.getCampaign().getUnit(uid); diff --git a/MekHQ/src/mekhq/gui/stratcon/StratconScenarioWizard.java b/MekHQ/src/mekhq/gui/stratcon/StratconScenarioWizard.java index b305727c8e..de1869a70b 100644 --- a/MekHQ/src/mekhq/gui/stratcon/StratconScenarioWizard.java +++ b/MekHQ/src/mekhq/gui/stratcon/StratconScenarioWizard.java @@ -470,7 +470,7 @@ private void btnCommitClicked(ActionEvent e) { } } - currentScenario.addForce(force, templateID); + currentScenario.addForce(force, templateID, campaign); } } From d1690eff6d558fad73a49505c82e943eb44d9c41 Mon Sep 17 00:00:00 2001 From: Weaver Date: Sun, 20 Oct 2024 13:17:57 -0700 Subject: [PATCH 080/118] Force - add doc comment --- MekHQ/src/mekhq/campaign/force/Force.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/MekHQ/src/mekhq/campaign/force/Force.java b/MekHQ/src/mekhq/campaign/force/Force.java index 6fb6ce545a..ce602f978f 100644 --- a/MekHQ/src/mekhq/campaign/force/Force.java +++ b/MekHQ/src/mekhq/campaign/force/Force.java @@ -183,6 +183,11 @@ public int getScenarioId() { return scenarioId; } + /** + * Set scenario ID (e.g. deploy to scenario) for a force and all of its subforces and units + * @param scenarioId scenario to deploy to + * @param campaign campaign - required to update units + */ public void setScenarioId(int scenarioId, Campaign campaign) { this.scenarioId = scenarioId; for (Force sub : getSubForces()) { From d7b5847dabee0fe4159494aba6542503e4555ea2 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 15:59:21 -0500 Subject: [PATCH 081/118] Improved Force Gen Unit Substitution Updated the `generateLance` methods to include the `allowsTanks` parameter to allow us to ensure Vees aren't substituted into environments they cannot survive. Enhanced logging and fallback mechanisms were also integrated to handle unit generation failures and substitutions more effectively. --- .../mission/AtBDynamicScenarioFactory.java | 184 +++++++----------- 1 file changed, 74 insertions(+), 110 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java index 534fd6ca72..fc230705da 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java @@ -645,7 +645,7 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac if (unitWeights != null) { generatedLance = generateLance(factionCode, skill, quality, unitTypes, unitWeights, - requiredRoles, campaign, scenario); + requiredRoles, campaign, scenario, allowsTanks); } else { generatedLance = new ArrayList<>(); } @@ -656,7 +656,7 @@ public static int generateForce(AtBDynamicScenario scenario, AtBContract contrac } } else { generatedLance = generateLance(factionCode, skill, quality, unitTypes, requiredRoles, - campaign, scenario); + campaign); // If extreme temperatures are present and XCT infantry is not being generated, // swap out standard armor for snowsuits or heat suits as appropriate @@ -2825,83 +2825,29 @@ public static int randomForceWeight() { } /** - * Generates a lance or similar tactical grouping (star, Level II, etc.) of - * entities with given - * parameters. - * This overload is included as a convenience for when weight class is not - * important or doesn't - * apply to the desired entity types. + * Generates a lance or similar tactical grouping (star, Level II, etc.) of entities with the given parameters. + * This overload acts as a convenience method for situations where weight class is not applicable + * or is not an important factor in the desired entity types. * - * @param faction The faction from which to generate entities. - * @param skill {@link SkillLevel} target for all units - * @param quality Quality of the units. - * @param unitTypes List of {@link UnitType}, one for each unit to generate - * @param rolesByType Collections of roles required for each unit type - * @param campaign working campaign. - * @return List of entities created for this lance/tactical group + * @param faction The faction code from which to generate entities. + * @param skill The targeted {@link SkillLevel} for all units. + * @param quality The quality of the units. + * @param unitTypes A {@link List} of {@link UnitType} integers, each entry corresponds to each + * unit to be created. + * @param rolesByType A {@link Map} wherein the key is a unit type and the value is a {@link Collection} + * of roles required for that unit type. + * @param campaign The current {@link Campaign}. + * @return A {@link List} of {@link Entity} objects created for this lance or a tactical group. */ - private static List generateLance(String faction, - SkillLevel skill, - int quality, - List unitTypes, - Map> rolesByType, - Campaign campaign, Scenario scenario) { + private static List generateLance(String faction, SkillLevel skill, int quality, + List unitTypes, Map> rolesByType, + Campaign campaign) { List generatedEntities = new ArrayList<>(); - for (int i = 0; i < unitTypes.size(); i++) { - Entity newEntity = getEntity(faction, - skill, - quality, - unitTypes.get(i), - UNIT_WEIGHT_UNSPECIFIED, - rolesByType.getOrDefault(unitTypes.get(i), new ArrayList<>()), - campaign); - - if (newEntity == null) { - // We reset the count locally, so that we're passing through the entire list. - // The idea is that this will give us the best chance of hitting a valid unit. - int freshIteration = 0; - - while (freshIteration < unitTypes.size()) { - newEntity = getEntity(faction, - skill, - quality, - unitTypes.get(i), - UNIT_WEIGHT_UNSPECIFIED, - rolesByType.getOrDefault(unitTypes.get(i), new ArrayList<>()), - campaign); - - if (newEntity != null) { - break; - } - - freshIteration++; - } - - // If we still haven't got a valid entity, use hardcoded fallbacks. - if (newEntity == null) { - if (unitTypes.get(0) == UnitType.DROPSHIP) { - newEntity = getEntity(faction, - skill, - quality, - UnitType.DROPSHIP, - UNIT_WEIGHT_UNSPECIFIED, - List.of(CIVILIAN), - campaign); - } else { - if (scenario.getBoardType() == T_GROUND) { - getEntity(faction, - skill, - quality, - UnitType.TANK, - UNIT_WEIGHT_UNSPECIFIED, - null, - campaign); - } - } - } - } + for (Integer unitType : unitTypes) { + Entity newEntity = getEntity(faction, skill, quality, unitType, UNIT_WEIGHT_UNSPECIFIED, + rolesByType.getOrDefault(unitType, new ArrayList<>()), campaign); if (newEntity != null) { generatedEntities.add(newEntity); @@ -2912,32 +2858,28 @@ private static List generateLance(String faction, } /** - * Generates a lance or similar tactical grouping (star, Level II, etc.) of - * entities with given - * parameters. The number of entities generated is the lowest of number of unit - * types or number - * of provided weight classes. - * - * @param faction The faction from which to generate entities. - * @param skill {@link SkillLevel} target for all units - * @param quality Quality of the units. - * @param unitTypes List of {@link UnitType}, one for each unit to generate; - * should be the same - * length as list of weights - * @param weights Weight class string suitable for - * AtBConfiguration.decodeWeightStr - * e.g. "LMMH" generates one light, two medium, and one heavy - * @param rolesByType Collections of roles required for each unit type - * @param campaign Working campaign - * @return List of entities created for this lance/tactical group - */ - private static List generateLance(String faction, - SkillLevel skill, - int quality, - List unitTypes, - String weights, - Map> rolesByType, - Campaign campaign, AtBScenario scenario) { + * Generates a lance or similar tactical grouping (star, Level II, etc.) of entities with given parameters. + * The number of entities generated is tapered by the smallest length between the number of unit + * types and the number of provided weight classes. + * + * @param faction The faction code from which to generate entities. + * @param skill The targeted {@link SkillLevel} for all units. + * @param quality The quality of the units. + * @param unitTypes A {@link List} of {@link UnitType} integers, should contain one entry + * for each unit to generate. + * @param weights A weight class string suitable for AtBConfiguration.decodeWeightStr + * (e.g., "LMMH" generates one light, two medium, and one heavy). + * @param rolesByType A {@link Map} where the key is a unit type and the value is a {@link Collection} + * of roles required for that unit type. + * @param campaign The working {@link Campaign}. + * @param scenario The {@link AtBScenario} the generated lance is for. + * @param allowsTanks A boolean flag indicating whether the generation allows tanks. + * @return A {@link List} of {@link Entity} objects created for this lance or a + * tactical group. + */ + private static List generateLance(String faction, SkillLevel skill, int quality, + List unitTypes, String weights, Map> rolesByType, + Campaign campaign, AtBScenario scenario, boolean allowsTanks) { List generatedEntities = new ArrayList<>(); // If the number of unit types and number of weight classes don't match, @@ -2957,33 +2899,55 @@ private static List generateLance(String faction, campaign, i); if (newEntity == null) { - // We reset the count locally, so that we're passing through the entire list. - // The idea is that this will give us the best chance of hitting a valid unit. - int freshIteration = 0; + logger.info(String.format("Failed to generate unit of type %s, weight %s. Beginning substitution.", + unitTypes.get(i), AtBConfiguration.decodeWeightStr(weights, i))); + + // If we've failed to get an entity, we start adjusting weight categories to see + // if we hit something valid. + // We start at lighter weights as, generally, they're less impactful than heavier units. + List individualType = List.of(unitTypes.get(i)); + + Map> individualRole = new HashMap<>(); + individualRole.put(0, rolesByType.getOrDefault(unitTypes.get(i), List.of())); - while (freshIteration < unitTypeSize) { - newEntity = getNewEntity(faction, skill, quality, unitTypes, weights, rolesByType, - campaign, freshIteration); + List weightClasses = List.of("UL", "L", "M", "H", "A"); + + for (String weight : weightClasses) { + newEntity = getNewEntity(faction, skill, quality, individualType, weight, individualRole, campaign, 0); if (newEntity != null) { + logger.info(String.format("Substitution successful. Substituted with %s.", + weight)); break; } - - freshIteration++; } // If we still haven't got a valid entity, use hardcoded fallbacks. + logger.info("Substitution unsuccessful. Using hardcoded fallbacks"); + if (newEntity == null) { if (unitTypes.get(0) == UnitType.DROPSHIP) { - newEntity = getNewEntity(faction, skill, quality, List.of(UnitType.TANK), + newEntity = getNewEntity(faction, skill, quality, List.of(UnitType.DROPSHIP), weights, Map.of(UnitType.DROPSHIP, List.of(CIVILIAN)), campaign, 0); + + if (newEntity != null) { + logger.info("Substitution successful. Substituted with Civilian DropShip."); + } } else { - if (scenario.getBoardType() == T_GROUND) { + if (scenario.getBoardType() == T_GROUND && allowsTanks) { newEntity = getNewEntity(faction, skill, quality, List.of(UnitType.TANK), weights, null, campaign, 0); + + if (newEntity != null) { + logger.info("Substitution successful. Substituted with Tank."); + } } } + + if (newEntity == null) { + logger.info("Substitution unsuccessful. Abandoning attempts to generate unit."); + } } } From 2618bcc0fc6484d1b4fa6480362952b604a3a0f8 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 16:04:26 -0500 Subject: [PATCH 082/118] Log force generation for debugging Added a log statement to track force generation in the AtBDynamicScenarioFactory. This helps in identifying issues by providing insights into the creation of forces based on templates. --- MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java | 1 + 1 file changed, 1 insertion(+) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java index fc230705da..f679304fbe 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java @@ -284,6 +284,7 @@ private static int generateForces(AtBDynamicScenario scenario, AtBContract contr } else { int weightClass = randomForceWeight(); + logger.info(String.format("++ Generating a force for the %s template ++", forceTemplate.getForceName()).toUpperCase()); generatedLanceCount += generateForce(scenario, contract, campaign, effectiveBV, effectiveUnitCount, weightClass, forceTemplate, false); } From dfc093f46a4424fe401bbee7ec5c9fa8dd15be8e Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 16:19:37 -0500 Subject: [PATCH 083/118] Refined `PartQualityReportDialog` Layout Handling and Exclusions Updated the layout for `PartQualityReportDialog` by calling `pack()`` and setting the dialog to modal. Excluded `AmmoBin` parts from report generation and dynamically scaled font sizes using `UIUtil` for improved UI adaptability. --- .../PartQualityReportDialog.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/MekHQ/src/mekhq/gui/dialog/reportDialogs/PartQualityReportDialog.java b/MekHQ/src/mekhq/gui/dialog/reportDialogs/PartQualityReportDialog.java index f10130ddeb..43e1fb476c 100644 --- a/MekHQ/src/mekhq/gui/dialog/reportDialogs/PartQualityReportDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/reportDialogs/PartQualityReportDialog.java @@ -18,9 +18,9 @@ */ package mekhq.gui.dialog.reportDialogs; -import mekhq.MekHQ; +import megamek.client.ui.swing.util.UIUtil; import mekhq.campaign.parts.Part; -import mekhq.campaign.parts.enums.PartQuality; +import mekhq.campaign.parts.equipment.AmmoBin; import mekhq.campaign.unit.Unit; import mekhq.utilities.ReportingUtilities; @@ -51,6 +51,8 @@ public PartQualityReportDialog(final JFrame frame, final Unit unit) { setTitle(String.format(resources.getString("PartQualityReportDialog.Unit.title"), unit.getName())); initialize(); + pack(); + setModal(true); } //endregion Constructors @@ -87,6 +89,10 @@ private String getPartsReport(Unit unit) { // Iterate over parts, assigning each to its location in the map. for (Part part : unit.getParts()) { + if (part instanceof AmmoBin) { + continue; + } + String location = part.getLocationName() != null ? part.getLocationName() : unit.getName(); reportMap.computeIfAbsent(location, k -> new ArrayList<>()).add(part); } @@ -113,17 +119,18 @@ private String getPartsReport(Unit unit) { String colorCode = unit.getQuality().getHexColor(); // Add the location and its colored quality rating to the report. - report.append("") + int headerFontSize = UIUtil.scaleForGUI(18); + report.append("") .append(location) .append(" - "); - report.append("") + report.append("") .append(unit.getQualityName()) .append(""); report.append(""); } else { - report.append("").append(location).append(""); + int headerFontSize = UIUtil.scaleForGUI(12); + report.append("") + .append(location).append(""); } report.append("

"); From ac97987ed2c8530bda87e5d1f0b4542ecc9f22f9 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 16:27:26 -0500 Subject: [PATCH 084/118] Fixed SPA Tooltip HTML Wrapping in `PersonViewPanel` Removed the unnecessary "" tags from tooltip descriptions in `PersonViewPanel`. --- MekHQ/src/mekhq/gui/view/PersonViewPanel.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java index afa2b71c4a..ec30149480 100644 --- a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java @@ -1449,9 +1449,8 @@ private JPanel fillSkills() { IOption option = i.nextElement(); if (option.booleanValue()) { JLabel lblAbility2 = new JLabel(Utilities.getOptionDisplayName(option)); - lblAbility2.setToolTipText(wordWrap("" - + option.getDescription().replaceAll("\\n", "
") - + "")); + lblAbility2.setToolTipText(wordWrap( + option.getDescription().replaceAll("\\n", "
"))); lblAbility2.setName("lblAbility2"); lblAbility2.getAccessibleContext().getAccessibleRelationSet().add( new AccessibleRelation(AccessibleRelation.LABELED_BY, lblAbility1)); @@ -1482,9 +1481,8 @@ private JPanel fillSkills() { if (option.booleanValue()) { JLabel lblImplants2 = new JLabel(Utilities.getOptionDisplayName(option)); - lblImplants2.setToolTipText(wordWrap("" - + option.getDescription().replaceAll("\\n", "
") - + "")); + lblImplants2.setToolTipText(wordWrap( + option.getDescription().replaceAll("\\n", "
"))); lblImplants2.setName("lblImplants2"); lblImplants2.getAccessibleContext().getAccessibleRelationSet().add( new AccessibleRelation(AccessibleRelation.LABELED_BY, lblImplants1)); From 4cb8b0a64765b18bbe6ea7175439c52e47cb1224 Mon Sep 17 00:00:00 2001 From: Weaver Date: Sun, 20 Oct 2024 15:00:47 -0700 Subject: [PATCH 085/118] Fix an errant apostraphe --- MekHQ/src/mekhq/campaign/parts/Part.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/parts/Part.java b/MekHQ/src/mekhq/campaign/parts/Part.java index 71a4187e9a..1ff036c321 100644 --- a/MekHQ/src/mekhq/campaign/parts/Part.java +++ b/MekHQ/src/mekhq/campaign/parts/Part.java @@ -1048,7 +1048,7 @@ public String fail(int rating) { timeSpent = 0; shorthandedMod = 0; return ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "' failed") + "."; + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), " failed") + "."; } @Override From 738cb7446e42f6346700285012be714ab8ec080b Mon Sep 17 00:00:00 2001 From: Weaver Date: Sun, 20 Oct 2024 16:06:02 -0700 Subject: [PATCH 086/118] Fixes for MissingPart and MissingAmmoBin --- .../src/mekhq/campaign/parts/MissingPart.java | 42 ++++++++++++------- .../parts/equipment/MissingAmmoBin.java | 4 +- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/parts/MissingPart.java b/MekHQ/src/mekhq/campaign/parts/MissingPart.java index 5c9350c067..812d33681f 100644 --- a/MekHQ/src/mekhq/campaign/parts/MissingPart.java +++ b/MekHQ/src/mekhq/campaign/parts/MissingPart.java @@ -26,6 +26,7 @@ import mekhq.MekHQ; import mekhq.campaign.Campaign; import mekhq.campaign.finances.Money; +import mekhq.campaign.parts.equipment.MissingAmmoBin; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.unit.Unit; import mekhq.campaign.work.IAcquisitionWork; @@ -210,25 +211,34 @@ public String getDetails() { public String getDetails(boolean includeRepairDetails) { PartInventory inventories = campaign.getPartInventory(getNewPart()); StringBuilder toReturn = new StringBuilder(); + + String superDetails = super.getDetails(includeRepairDetails); + toReturn.append(superDetails); + + if (!(this instanceof MissingAmmoBin)) { + // Ammo bins don't require/have stock replacements. + if(!superDetails.isEmpty()) { + toReturn.append(", "); + } + if (isReplacementAvailable()) { + toReturn.append(inventories.getSupply()) + .append(" in stock"); + } else { + toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "None in stock")); + } - if (isReplacementAvailable()) { - toReturn.append(inventories.getSupply()) - .append(" in stock"); - } else { - toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "None in stock")); - } - - String incoming = inventories.getTransitOrderedDetails(); - if (!incoming.isEmpty()) { - StringBuilder incomingSB = new StringBuilder(); + String incoming = inventories.getTransitOrderedDetails(); + if (!incoming.isEmpty()) { + StringBuilder incomingSB = new StringBuilder(); - incomingSB.append(" (") - .append(incoming) - .append(")"); + incomingSB.append(" (") + .append(incoming) + .append(")"); - toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( - MekHQ.getMHQOptions().getFontColorWarningHexColor(), incomingSB.toString())); + toReturn.append(ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorWarningHexColor(), incomingSB.toString())); + } } return toReturn.toString(); } diff --git a/MekHQ/src/mekhq/campaign/parts/equipment/MissingAmmoBin.java b/MekHQ/src/mekhq/campaign/parts/equipment/MissingAmmoBin.java index 9e37ca762c..f04325bfb1 100644 --- a/MekHQ/src/mekhq/campaign/parts/equipment/MissingAmmoBin.java +++ b/MekHQ/src/mekhq/campaign/parts/equipment/MissingAmmoBin.java @@ -64,7 +64,7 @@ public AmmoType getType() { * pod space, so we're going to stick them in LOC_NONE where the heat sinks are */ @Override public String getLocationName() { - if (unit.getEntity() instanceof Aero + if ((null != unit) && (unit.getEntity() instanceof Aero) && !((unit.getEntity() instanceof SmallCraft) || (unit.getEntity() instanceof Jumpship))) { return "Fuselage"; } @@ -73,7 +73,7 @@ public String getLocationName() { @Override public int getLocation() { - if (unit.getEntity() instanceof Aero + if ((null != unit) && (unit.getEntity() instanceof Aero) && !((unit.getEntity() instanceof SmallCraft) || (unit.getEntity() instanceof Jumpship))) { return Aero.LOC_NONE; } From 7380b9d421112d66a5d9226b9ed40dedb00ac4a4 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 18:45:24 -0500 Subject: [PATCH 087/118] Switch to ordinal for enum serialization and deserialization Replaced `toString()` with `ordinal()` for enum serialization in `Person.java`. Updated deserialization to handle both ordinal and string formats for backward compatibility. Ensured correct parsing and logging in related classes. --- .../src/mekhq/campaign/personnel/Person.java | 53 +- .../education/EducationController.java | 3 +- .../randomEvents/PersonalityController.java | 27 +- .../enums/personalities/Aggression.java | 66 +-- .../enums/personalities/Ambition.java | 66 +-- .../enums/personalities/Greed.java | 66 +-- .../enums/personalities/Intelligence.java | 148 +---- .../enums/personalities/PersonalityQuirk.java | 541 +++++------------- .../enums/personalities/Social.java | 187 +----- 9 files changed, 279 insertions(+), 878 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/Person.java b/MekHQ/src/mekhq/campaign/personnel/Person.java index 934c25bc0a..91603a68df 100644 --- a/MekHQ/src/mekhq/campaign/personnel/Person.java +++ b/MekHQ/src/mekhq/campaign/personnel/Person.java @@ -2207,27 +2207,27 @@ public void writeToXML(final PrintWriter pw, int indent, final Campaign campaign } if (aggression != Aggression.NONE) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "aggression", aggression.toString()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "aggression", aggression.ordinal()); } if (ambition != Ambition.NONE) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "ambition", ambition.toString()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "ambition", ambition.ordinal()); } if (greed != Greed.NONE) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "greed", greed.toString()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "greed", greed.ordinal()); } if (social != Social.NONE) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "social", social.toString()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "social", social.ordinal()); } if (personalityQuirk != PersonalityQuirk.NONE) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "personalityQuirk", personalityQuirk.toString()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "personalityQuirk", personalityQuirk.ordinal()); } if (intelligence != Intelligence.AVERAGE) { - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "intelligence", intelligence.toString()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "intelligence", intelligence.ordinal()); } if (!StringUtility.isNullOrBlank(personalityDescription)) { @@ -2564,17 +2564,48 @@ public static Person generateInstanceFromXML(Node wn, Campaign c, Version versio } else if (wn2.getNodeName().equalsIgnoreCase("eduEducationTime")) { retVal.eduEducationTime = Integer.parseInt(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("aggression")) { - retVal.aggression = Aggression.parseFromString(wn2.getTextContent()); + try { + // <50.01 compatibility handler + retVal.aggression = Aggression.parseFromString(wn2.getTextContent()); + } catch (Exception e) { + retVal.aggression = Aggression.fromOrdinal(Integer.parseInt(wn2.getTextContent())); + } } else if (wn2.getNodeName().equalsIgnoreCase("ambition")) { - retVal.ambition = Ambition.parseFromString(wn2.getTextContent()); + try { + // <50.01 compatibility handler + retVal.ambition = Ambition.parseFromString(wn2.getTextContent()); + } catch (Exception e) { + retVal.ambition = Ambition.fromOrdinal(Integer.parseInt(wn2.getTextContent())); + } } else if (wn2.getNodeName().equalsIgnoreCase("greed")) { - retVal.greed = Greed.parseFromString(wn2.getTextContent()); + try { + // <50.01 compatibility handler + retVal.greed = Greed.parseFromString(wn2.getTextContent()); + } catch (Exception e) { + retVal.greed = Greed.fromOrdinal(Integer.parseInt(wn2.getTextContent())); + } } else if (wn2.getNodeName().equalsIgnoreCase("social")) { + try { + // <50.01 compatibility handler + retVal.social = Social.parseFromString(wn2.getTextContent()); + } catch (Exception e) { + retVal.social = Social.fromOrdinal(Integer.parseInt(wn2.getTextContent())); + } retVal.social = Social.parseFromString(wn2.getTextContent()); } else if (wn2.getNodeName().equalsIgnoreCase("personalityQuirk")) { - retVal.personalityQuirk = PersonalityQuirk.parseFromString(wn2.getTextContent()); + try { + // <50.01 compatibility handler + retVal.personalityQuirk = PersonalityQuirk.parseFromString(wn2.getTextContent()); + } catch (Exception e) { + retVal.personalityQuirk = PersonalityQuirk.fromOrdinal(Integer.parseInt(wn2.getTextContent())); + } } else if (wn2.getNodeName().equalsIgnoreCase("intelligence")) { - retVal.intelligence = Intelligence.parseFromString(wn2.getTextContent()); + try { + // <50.01 compatibility handler + retVal.intelligence = Intelligence.parseFromString(wn2.getTextContent()); + } catch (Exception e) { + retVal.intelligence = Intelligence.fromOrdinal(Integer.parseInt(wn2.getTextContent())); + } } else if (wn2.getNodeName().equalsIgnoreCase("personalityDescription")) { retVal.personalityDescription = wn2.getTextContent(); } else if (wn2.getNodeName().equalsIgnoreCase("clanPersonnel")) { diff --git a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java index cc4fc58969..d5d3ffaf72 100644 --- a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java +++ b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java @@ -32,7 +32,6 @@ import mekhq.campaign.personnel.enums.PersonnelStatus; import mekhq.campaign.personnel.enums.education.EducationLevel; import mekhq.campaign.personnel.enums.education.EducationStage; -import mekhq.campaign.personnel.randomEvents.enums.personalities.Intelligence; import mekhq.utilities.ReportingUtilities; import java.time.DayOfWeek; @@ -1357,7 +1356,7 @@ private static void graduateChild(Campaign campaign, Person person, Academy acad } if (campaign.getCampaignOptions().isUseRandomPersonalities()) { - graduationRoll += Intelligence.parseToInt(person.getIntelligence()) - 12; + graduationRoll += person.getIntelligence().ordinal() - 12; } if (graduationRoll < 30) { diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/PersonalityController.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/PersonalityController.java index 8545ce0125..0e01987197 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/PersonalityController.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/PersonalityController.java @@ -19,24 +19,15 @@ package mekhq.campaign.personnel.randomEvents; -import static mekhq.campaign.personnel.randomEvents.enums.personalities.Intelligence.*; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Random; - import megamek.common.Compute; import megamek.common.enums.Gender; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.enums.GenderDescriptors; -import mekhq.campaign.personnel.randomEvents.enums.personalities.Aggression; -import mekhq.campaign.personnel.randomEvents.enums.personalities.Ambition; -import mekhq.campaign.personnel.randomEvents.enums.personalities.Greed; -import mekhq.campaign.personnel.randomEvents.enums.personalities.Intelligence; -import mekhq.campaign.personnel.randomEvents.enums.personalities.PersonalityQuirk; -import mekhq.campaign.personnel.randomEvents.enums.personalities.Social; +import mekhq.campaign.personnel.randomEvents.enums.personalities.*; + +import java.util.*; + +import static mekhq.campaign.personnel.randomEvents.enums.personalities.Intelligence.*; public class PersonalityController { public static void generatePersonality(Person person) { @@ -97,10 +88,10 @@ private static void setPersonalityTrait(Person person, int tableRoll, int traitR } switch (tableRoll) { - case 0 -> person.setAggression(Aggression.parseFromInt(traitRoll)); - case 1 -> person.setAmbition(Ambition.parseFromInt(traitRoll)); - case 2 -> person.setGreed(Greed.parseFromInt(traitRoll)); - case 3 -> person.setSocial(Social.parseFromInt(traitRoll)); + case 0 -> person.setAggression(Aggression.fromOrdinal(traitRoll)); + case 1 -> person.setAmbition(Ambition.fromOrdinal(traitRoll)); + case 2 -> person.setGreed(Greed.fromOrdinal(traitRoll)); + case 3 -> person.setSocial(Social.fromOrdinal(traitRoll)); default -> throw new IllegalStateException( "Unexpected value in mekhq/campaign/personnel/randomEvents/personality/PersonalityController.java/setPersonalityTrait: " + tableRoll); diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java index a627885845..71240f83ac 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java @@ -18,10 +18,11 @@ */ package mekhq.campaign.personnel.randomEvents.enums.personalities; -import java.util.ResourceBundle; - +import megamek.logging.MMLogger; import mekhq.MekHQ; +import java.util.ResourceBundle; + public enum Aggression { // region Enum Declarations NONE("Personality.NONE.text", "Personality.NONE.description", false, false), @@ -238,7 +239,7 @@ public boolean isVigilant() { * @throws IllegalStateException if the given string does not match any valid * Aggression */ - + @Deprecated public static Aggression parseFromString(final String aggression) { return switch (aggression) { case "0", "None" -> NONE; @@ -282,54 +283,23 @@ public static Aggression parseFromString(final String aggression) { } /** - * Parses an integer value into an Aggression enum. + * Returns the {@link Aggression} associated with the given ordinal. * - * @param aggression the integer value representing the Aggression level - * @return the corresponding Aggression enum value - * @throws IllegalStateException if the integer value does not correspond to any - * valid Aggression enum value + * @param ordinal the ordinal value of the {@link Aggression} + * @return the {@link Aggression} associated with the given ordinal, or default value + * {@code NONE} if not found */ + public static Aggression fromOrdinal(int ordinal) { + for (Aggression aggression : values()) { + if (aggression.ordinal() == ordinal) { + return aggression; + } + } - public static Aggression parseFromInt(final int aggression) { - return switch (aggression) { - case 0 -> NONE; - // Minor Characteristics - case 1 -> BOLD; - case 2 -> AGGRESSIVE; - case 3 -> ASSERTIVE; - case 4 -> BELLIGERENT; - case 5 -> BRASH; - case 6 -> CONFIDENT; - case 7 -> COURAGEOUS; - case 8 -> DARING; - case 9 -> DECISIVE; - case 10 -> DETERMINED; - case 11 -> DOMINEERING; - case 12 -> FEARLESS; - case 13 -> HOSTILE; - case 14 -> HOT_HEADED; - case 15 -> IMPETUOUS; - case 16 -> IMPULSIVE; - case 17 -> INFLEXIBLE; - case 18 -> INTREPID; - case 19 -> OVERBEARING; - case 20 -> RECKLESS; - case 21 -> RESOLUTE; - case 22 -> STUBBORN; - case 23 -> TENACIOUS; - case 24 -> VIGILANT; - // Major Characteristics - case 25 -> BLOODTHIRSTY; - case 26 -> DIPLOMATIC; - case 27 -> MURDEROUS; - case 28 -> PACIFISTIC; - case 29 -> SADISTIC; - case 30 -> SAVAGE; - default -> - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/personnel/enums/randomEvents/personalities/Aggression.java/parseFromInt: " - + aggression); - }; + final MMLogger logger = MMLogger.create(Aggression.class); + logger.error(String.format("Unknown Aggression ordinal: %s - returning NONE.", ordinal)); + + return NONE; } @Override diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java index 044cf956d7..2b59f3a032 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java @@ -18,10 +18,11 @@ */ package mekhq.campaign.personnel.randomEvents.enums.personalities; -import java.util.ResourceBundle; - +import megamek.logging.MMLogger; import mekhq.MekHQ; +import java.util.ResourceBundle; + public enum Ambition { // region Enum Declarations NONE("Personality.NONE.text", "Personality.NONE.description", false, false), @@ -237,7 +238,7 @@ public boolean isVisionary() { * @throws IllegalStateException if the given string does not match any valid * Ambition */ - + @Deprecated public static Ambition parseFromString(final String ambition) { return switch (ambition) { case "0", "None" -> NONE; @@ -281,54 +282,23 @@ public static Ambition parseFromString(final String ambition) { } /** - * Parses an integer value into an Aggression enum. + * Returns the {@link Ambition} associated with the given ordinal. * - * @param ambition the integer value representing the Ambition level - * @return the corresponding Ambition enum value - * @throws IllegalStateException if the integer value does not correspond to any - * valid Ambition enum value + * @param ordinal the ordinal value of the {@link Ambition} + * @return the {@link Ambition} associated with the given ordinal, or default value + * {@code NONE} if not found */ + public static Ambition fromOrdinal(int ordinal) { + for (Ambition ambition : values()) { + if (ambition.ordinal() == ordinal) { + return ambition; + } + } - public static Ambition parseFromInt(final int ambition) { - return switch (ambition) { - case 0 -> NONE; - // Minor Characteristics - case 1 -> AMBITIOUS; - case 2 -> ARROGANT; - case 3 -> ASPIRING; - case 4 -> CALCULATING; - case 5 -> CONNIVING; - case 6 -> CONTROLLING; - case 7 -> CUTTHROAT; - case 8 -> DILIGENT; - case 9 -> DRIVEN; - case 10 -> ENERGETIC; - case 11 -> EXCESSIVE; - case 12 -> FOCUSED; - case 13 -> GOAL_ORIENTED; - case 14 -> MOTIVATED; - case 15 -> OPPORTUNISTIC; - case 16 -> OVERCONFIDENT; - case 17 -> PERSISTENT; - case 18 -> PROACTIVE; - case 19 -> RESILIENT; - case 20 -> RUTHLESS; - case 21 -> SELFISH; - case 22 -> STRATEGIC; - case 23 -> UNAMBITIOUS; - case 24 -> UNSCRUPULOUS; - // Major Characteristics - case 25 -> DISHONEST; - case 26 -> INNOVATIVE; - case 27 -> MANIPULATIVE; - case 28 -> RESOURCEFUL; - case 29 -> TYRANNICAL; - case 30 -> VISIONARY; - default -> - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/personnel/enums/randomEvents/personalities/Ambition.java/parseFromInt: " - + ambition); - }; + final MMLogger logger = MMLogger.create(Ambition.class); + logger.error(String.format("Unknown Ambition ordinal: %s - returning NONE.", ordinal)); + + return NONE; } @Override diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java index 5119e13a74..e0e9f2f5ac 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java @@ -18,10 +18,11 @@ */ package mekhq.campaign.personnel.randomEvents.enums.personalities; -import java.util.ResourceBundle; - +import megamek.logging.MMLogger; import mekhq.MekHQ; +import java.util.ResourceBundle; + public enum Greed { // region Enum Declarations NONE("Personality.NONE.text", "Personality.NONE.description", false, false), @@ -237,7 +238,7 @@ public boolean isVoracious() { * @throws IllegalStateException if the given string does not match any valid * Greed */ - + @Deprecated public static Greed parseFromString(final String greed) { return switch (greed) { case "0", "None" -> NONE; @@ -281,54 +282,23 @@ public static Greed parseFromString(final String greed) { } /** - * Parses an integer value into an Greed enum. + * Returns the {@link Greed} associated with the given ordinal. * - * @param greed the integer value representing the Greed level - * @return the corresponding Greed enum value - * @throws IllegalStateException if the integer value does not correspond to any - * valid Greed enum value + * @param ordinal the ordinal value of the {@link Greed} + * @return the {@link Greed} associated with the given ordinal, or default value + * {@code NONE} if not found */ + public static Greed fromOrdinal(int ordinal) { + for (Greed greed : values()) { + if (greed.ordinal() == ordinal) { + return greed; + } + } - public static Greed parseFromInt(final int greed) { - return switch (greed) { - case 0 -> NONE; - // Minor Characteristics - case 1 -> ASTUTE; - case 2 -> ADEPT; - case 3 -> AVARICIOUS; - case 4 -> DYNAMIC; - case 5 -> EAGER; - case 6 -> EXPLOITATIVE; - case 7 -> FRAUDULENT; - case 8 -> GENEROUS; - case 9 -> GREEDY; - case 10 -> HOARDING; - case 11 -> INSATIABLE; - case 12 -> INSIGHTFUL; - case 13 -> JUDICIOUS; - case 14 -> LUSTFUL; - case 15 -> MERCENARY; - case 16 -> OVERREACHING; - case 17 -> PROFITABLE; - case 18 -> SAVVY; - case 19 -> SELF_SERVING; - case 20 -> SHAMELESS; - case 21 -> SHREWD; - case 22 -> TACTICAL; - case 23 -> UNPRINCIPLED; - case 24 -> VORACIOUS; - // Major Characteristics - case 25 -> CORRUPT; - case 26 -> ENTERPRISING; - case 27 -> INTUITIVE; - case 28 -> METICULOUS; - case 29 -> NEFARIOUS; - case 30 -> THIEF; - default -> - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/personnel/enums/randomEvents/personalities/Greed.java/parseFromInt: " - + greed); - }; + final MMLogger logger = MMLogger.create(Greed.class); + logger.error(String.format("Unknown Greed ordinal: %s - returning NONE.", ordinal)); + + return NONE; } @Override diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Intelligence.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Intelligence.java index 7237a0bb6e..f05c9b31b5 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Intelligence.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Intelligence.java @@ -18,10 +18,11 @@ */ package mekhq.campaign.personnel.randomEvents.enums.personalities; -import java.util.ResourceBundle; - +import megamek.logging.MMLogger; import mekhq.MekHQ; +import java.util.ResourceBundle; + public enum Intelligence { // region Enum Declarations BRAIN_DEAD("Intelligence.BRAIN_DEAD.text", "Intelligence.BRAIN_DEAD.description"), @@ -73,106 +74,9 @@ public String getDescription() { // endregion Getters // region Boolean Comparison Methods - - public boolean isBrainDead() { - return this == BRAIN_DEAD; - } - - public boolean isUnintelligent() { - return this == UNINTELLIGENT; - } - - public boolean isFeebleMinded() { - return this == FOOLISH; - } - - public boolean isSimple() { - return this == SIMPLE; - } - - public boolean isSlow() { - return this == SLOW; - } - - public boolean isUninspired() { - return this == UNINSPIRED; - } - - public boolean isDull() { - return this == DULL; - } - - public boolean isDimwitted() { - return this == DIMWITTED; - } - - public boolean isObtuse() { - return this == OBTUSE; - } - - public boolean isBelowAverage() { - return this == BELOW_AVERAGE; - } - - public boolean isUnderPerforming() { - return this == UNDER_PERFORMING; - } - - public boolean isLimitedInsight() { - return this == LIMITED_INSIGHT; - } - public boolean isAverage() { return this == AVERAGE; } - - public boolean isAboveAverage() { - return this == ABOVE_AVERAGE; - } - - public boolean isSTUDIOUS() { - return this == STUDIOUS; - } - - public boolean isDiscerning() { - return this == DISCERNING; - } - - public boolean isSharp() { - return this == SHARP; - } - - public boolean isQuickWitted() { - return this == QUICK_WITTED; - } - - public boolean isPerceptive() { - return this == PERCEPTIVE; - } - - public boolean isBright() { - return this == BRIGHT; - } - - public boolean isClever() { - return this == CLEVER; - } - - public boolean isIntellectual() { - return this == INTELLECTUAL; - } - - public boolean isBrilliant() { - return this == BRILLIANT; - } - - public boolean isExceptional() { - return this == EXCEPTIONAL; - } - - public boolean isGenius() { - return this == GENIUS; - } // endregion Boolean Comparison Methods // region File I/O @@ -185,6 +89,7 @@ public boolean isGenius() { * @throws IllegalStateException if the given string does not match any valid * Quirk */ + @Deprecated public static Intelligence parseFromString(final String quirk) { return switch (quirk) { case "0", "Brain Dead" -> BRAIN_DEAD; @@ -220,40 +125,23 @@ public static Intelligence parseFromString(final String quirk) { } /** - * Parses the given Intelligence enum value to an integer. + * Returns the {@link Intelligence} associated with the given ordinal. * - * @param intelligence the Intelligence enum value to be parsed - * @return the integer value representing the parsed Intelligence + * @param ordinal the ordinal value of the {@link Intelligence} + * @return the {@link Intelligence} associated with the given ordinal, or default value + * {@code AVERAGE} if not found */ + public static Intelligence fromOrdinal(int ordinal) { + for (Intelligence intelligence : values()) { + if (intelligence.ordinal() == ordinal) { + return intelligence; + } + } - public static int parseToInt(final Intelligence intelligence) { - return switch (intelligence) { - case BRAIN_DEAD -> 0; - case UNINTELLIGENT -> 1; - case FOOLISH -> 2; - case SIMPLE -> 3; - case SLOW -> 4; - case UNINSPIRED -> 5; - case DULL -> 6; - case DIMWITTED -> 7; - case OBTUSE -> 8; - case BELOW_AVERAGE -> 9; - case UNDER_PERFORMING -> 10; - case LIMITED_INSIGHT -> 11; - case AVERAGE -> 12; - case ABOVE_AVERAGE -> 13; - case STUDIOUS -> 14; - case DISCERNING -> 15; - case SHARP -> 16; - case QUICK_WITTED -> 17; - case PERCEPTIVE -> 18; - case BRIGHT -> 19; - case CLEVER -> 20; - case INTELLECTUAL -> 21; - case BRILLIANT -> 22; - case EXCEPTIONAL -> 23; - case GENIUS -> 24; - }; + final MMLogger logger = MMLogger.create(Intelligence.class); + logger.error(String.format("Unknown Intelligence ordinal: %s - returning AVERAGE.", ordinal)); + + return AVERAGE; } @Override diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/PersonalityQuirk.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/PersonalityQuirk.java index eea4a4d1bb..b78e4c42c2 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/PersonalityQuirk.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/PersonalityQuirk.java @@ -18,10 +18,11 @@ */ package mekhq.campaign.personnel.randomEvents.enums.personalities; -import java.util.ResourceBundle; - +import megamek.logging.MMLogger; import mekhq.MekHQ; +import java.util.ResourceBundle; + public enum PersonalityQuirk { // region Enum Declarations NONE("Personality.NONE.text", "Personality.NONE.description"), @@ -129,7 +130,118 @@ public enum PersonalityQuirk { WEATHERMAN("PersonalityQuirk.WEATHERMAN.text", "PersonalityQuirk.WEATHERMAN.description"), WHISTLER("PersonalityQuirk.WHISTLER.text", "PersonalityQuirk.WHISTLER.description"), WORRIER("PersonalityQuirk.WORRIER.text", "PersonalityQuirk.WORRIER.description"), - WRITER("PersonalityQuirk.WRITER.text", "PersonalityQuirk.WRITER.description"); + WRITER("PersonalityQuirk.WRITER.text", "PersonalityQuirk.WRITER.description"), + BATTLEFIELD_NOSTALGIA("PersonalityQuirk.BATTLEFIELD_NOSTALGIA.text", "PersonalityQuirk.BATTLEFIELD_NOSTALGIA.description"), + HEAVY_HANDED("PersonalityQuirk.HEAVY_HANDED.text", "PersonalityQuirk.HEAVY_HANDED.description"), + RATION_HOARDER("PersonalityQuirk.RATION_HOARDER.text", "PersonalityQuirk.RATION_HOARDER.description"), + EMERGENCY_MANUAL_READER("PersonalityQuirk.EMERGENCY_MANUAL_READER.text", "PersonalityQuirk.EMERGENCY_MANUAL_READER.description"), + QUICK_TO_QUIP("PersonalityQuirk.QUICK_TO_QUIP.text", "PersonalityQuirk.QUICK_TO_QUIP.description"), + TECH_SKEPTIC("PersonalityQuirk.TECH_SKEPTIC.text", "PersonalityQuirk.TECH_SKEPTIC.description"), + POST_BATTLE_RITUALS("PersonalityQuirk.POST_BATTLE_RITUALS.text", "PersonalityQuirk.POST_BATTLE_RITUALS.description"), + OVER_COMMUNICATOR("PersonalityQuirk.OVER_COMMUNICATOR.text", "PersonalityQuirk.OVER_COMMUNICATOR.description"), + FIELD_MEDIC("PersonalityQuirk.FIELD_MEDIC.text", "PersonalityQuirk.FIELD_MEDIC.description"), + SYSTEM_CALIBRATOR("PersonalityQuirk.SYSTEM_CALIBRATOR.text", "PersonalityQuirk.SYSTEM_CALIBRATOR.description"), + AMMO_COUNTER("PersonalityQuirk.AMMO_COUNTER.text", "PersonalityQuirk.AMMO_COUNTER.description"), + BRAVADO("PersonalityQuirk.BRAVADO.text", "PersonalityQuirk.BRAVADO.description"), + COMBAT_SONG("PersonalityQuirk.COMBAT_SONG.text", "PersonalityQuirk.COMBAT_SONG.description"), + COMMS_TOGGLE("PersonalityQuirk.COMMS_TOGGLE.text", "PersonalityQuirk.COMMS_TOGGLE.description"), + EJECTION_READY("PersonalityQuirk.EJECTION_READY.text", "PersonalityQuirk.EJECTION_READY.description"), + HAND_SIGNS("PersonalityQuirk.HAND_SIGNS.text", "PersonalityQuirk.HAND_SIGNS.description"), + HATE_FOR_MEKS("PersonalityQuirk.HATE_FOR_MEKS.text", "PersonalityQuirk.HATE_FOR_MEKS.description"), + IMPROVISED_WEAPONRY("PersonalityQuirk.IMPROVISED_WEAPONRY.text", "PersonalityQuirk.IMPROVISED_WEAPONRY.description"), + PRE_BATTLE_SUPERSTITIONS("PersonalityQuirk.PRE_BATTLE_SUPERSTITIONS.text", "PersonalityQuirk.PRE_BATTLE_SUPERSTITIONS.description"), + SILENT_LEADER("PersonalityQuirk.SILENT_LEADER.text", "PersonalityQuirk.SILENT_LEADER.description"), + BATTLE_CRITIC("PersonalityQuirk.BATTLE_CRITIC.text", "PersonalityQuirk.BATTLE_CRITIC.description"), + CHECKS_WEAPON_SAFETY("PersonalityQuirk.CHECKS_WEAPON_SAFETY.text", "PersonalityQuirk.CHECKS_WEAPON_SAFETY.description"), + CLOSE_COMBAT_PREF("PersonalityQuirk.CLOSE_COMBAT_PREF.text", "PersonalityQuirk.CLOSE_COMBAT_PREF.description"), + COMBAT_POET("PersonalityQuirk.COMBAT_POET.text", "PersonalityQuirk.COMBAT_POET.description"), + CUSTOM_DECALS("PersonalityQuirk.CUSTOM_DECALS.text", "PersonalityQuirk.CUSTOM_DECALS.description"), + DISPLAYS_TROPHIES("PersonalityQuirk.DISPLAYS_TROPHIES.text", "PersonalityQuirk.DISPLAYS_TROPHIES.description"), + DO_IT_YOURSELF("PersonalityQuirk.DO_IT_YOURSELF.text", "PersonalityQuirk.DO_IT_YOURSELF.description"), + FIELD_IMPROVISER("PersonalityQuirk.FIELD_IMPROVISER.text", "PersonalityQuirk.FIELD_IMPROVISER.description"), + LOUD_COMMS("PersonalityQuirk.LOUD_COMMS.text", "PersonalityQuirk.LOUD_COMMS.description"), + WAR_STORIES("PersonalityQuirk.WAR_STORIES.text", "PersonalityQuirk.WAR_STORIES.description"), + ALL_OR_NOTHING("PersonalityQuirk.ALL_OR_NOTHING.text", "PersonalityQuirk.ALL_OR_NOTHING.description"), + BOOTS_ON_THE_GROUND("PersonalityQuirk.BOOTS_ON_THE_GROUND.text", "PersonalityQuirk.BOOTS_ON_THE_GROUND.description"), + BRAVERY_BOASTER("PersonalityQuirk.BRAVERY_BOASTER.text", "PersonalityQuirk.BRAVERY_BOASTER.description"), + COCKPIT_DRIFTER("PersonalityQuirk.COCKPIT_DRIFTER.text", "PersonalityQuirk.COCKPIT_DRIFTER.description"), + CONSPIRACY_THEORIST("PersonalityQuirk.CONSPIRACY_THEORIST.text", "PersonalityQuirk.CONSPIRACY_THEORIST.description"), + DEVOUT_WARRIOR("PersonalityQuirk.DEVOUT_WARRIOR.text", "PersonalityQuirk.DEVOUT_WARRIOR.description"), + DUAL_WIELDING("PersonalityQuirk.DUAL_WIELDING.text", "PersonalityQuirk.DUAL_WIELDING.description"), + EMBLEM_LOVER("PersonalityQuirk.EMBLEM_LOVER.text", "PersonalityQuirk.EMBLEM_LOVER.description"), + EXCESSIVE_DEBRIEFING("PersonalityQuirk.EXCESSIVE_DEBRIEFING.text", "PersonalityQuirk.EXCESSIVE_DEBRIEFING.description"), + EYE_FOR_ART("PersonalityQuirk.EYE_FOR_ART.text", "PersonalityQuirk.EYE_FOR_ART.description"), + FAST_TALKER("PersonalityQuirk.FAST_TALKER.text", "PersonalityQuirk.FAST_TALKER.description"), + FINGER_GUNS("PersonalityQuirk.FINGER_GUNS.text", "PersonalityQuirk.FINGER_GUNS.description"), + FLARE_DEPLOYER("PersonalityQuirk.FLARE_DEPLOYER.text", "PersonalityQuirk.FLARE_DEPLOYER.description"), + FRIENDLY_INTERROGATOR("PersonalityQuirk.FRIENDLY_INTERROGATOR.text", "PersonalityQuirk.FRIENDLY_INTERROGATOR.description"), + GUN_NUT("PersonalityQuirk.GUN_NUT.text", "PersonalityQuirk.GUN_NUT.description"), + LAST_MAN_STANDING("PersonalityQuirk.LAST_MAN_STANDING.text", "PersonalityQuirk.LAST_MAN_STANDING.description"), + LEGENDARY_MEK("PersonalityQuirk.LEGENDARY_MEK.text", "PersonalityQuirk.LEGENDARY_MEK.description"), + PASSIVE_LEADER("PersonalityQuirk.PASSIVE_LEADER.text", "PersonalityQuirk.PASSIVE_LEADER.description"), + REBEL_WITHOUT_CAUSE("PersonalityQuirk.REBEL_WITHOUT_CAUSE.text", "PersonalityQuirk.REBEL_WITHOUT_CAUSE.description"), + SIMPLE_LIFE("PersonalityQuirk.SIMPLE_LIFE.text", "PersonalityQuirk.SIMPLE_LIFE.description"), + ANTI_AUTHORITY("PersonalityQuirk.ANTI_AUTHORITY.text", "PersonalityQuirk.ANTI_AUTHORITY.description"), + BLOODLUST("PersonalityQuirk.BLOODLUST.text", "PersonalityQuirk.BLOODLUST.description"), + BRAVERY_IN_DOUBT("PersonalityQuirk.BRAVERY_IN_DOUBT.text", "PersonalityQuirk.BRAVERY_IN_DOUBT.description"), + CLOSE_QUARTERS_ONLY("PersonalityQuirk.CLOSE_QUARTERS_ONLY.text", "PersonalityQuirk.CLOSE_QUARTERS_ONLY.description"), + COOL_UNDER_FIRE("PersonalityQuirk.COOL_UNDER_FIRE.text", "PersonalityQuirk.COOL_UNDER_FIRE.description"), + CRASH_TEST("PersonalityQuirk.CRASH_TEST.text", "PersonalityQuirk.CRASH_TEST.description"), + DEAD_PAN_HUMOR("PersonalityQuirk.DEAD_PAN_HUMOR.text", "PersonalityQuirk.DEAD_PAN_HUMOR.description"), + DRILLS("PersonalityQuirk.DRILLS.text", "PersonalityQuirk.DRILLS.description"), + ENEMY_RESPECT("PersonalityQuirk.ENEMY_RESPECT.text", "PersonalityQuirk.ENEMY_RESPECT.description"), + EXTREME_MORNING_PERSON("PersonalityQuirk.EXTREME_MORNING_PERSON.text", "PersonalityQuirk.EXTREME_MORNING_PERSON.description"), + GALLANT("PersonalityQuirk.GALLANT.text", "PersonalityQuirk.GALLANT.description"), + IRON_STOMACH("PersonalityQuirk.IRON_STOMACH.text", "PersonalityQuirk.IRON_STOMACH.description"), + MISSION_CRITIC("PersonalityQuirk.MISSION_CRITIC.text", "PersonalityQuirk.MISSION_CRITIC.description"), + NO_PAIN_NO_GAIN("PersonalityQuirk.NO_PAIN_NO_GAIN.text", "PersonalityQuirk.NO_PAIN_NO_GAIN.description"), + PERSONAL_ARMORY("PersonalityQuirk.PERSONAL_ARMORY.text", "PersonalityQuirk.PERSONAL_ARMORY.description"), + QUICK_ADAPTER("PersonalityQuirk.QUICK_ADAPTER.text", "PersonalityQuirk.QUICK_ADAPTER.description"), + RETALIATOR("PersonalityQuirk.RETALIATOR.text", "PersonalityQuirk.RETALIATOR.description"), + RUSH_HOUR("PersonalityQuirk.RUSH_HOUR.text", "PersonalityQuirk.RUSH_HOUR.description"), + SILENT_PROTECTOR("PersonalityQuirk.SILENT_PROTECTOR.text", "PersonalityQuirk.SILENT_PROTECTOR.description"), + ALWAYS_TACTICAL("PersonalityQuirk.ALWAYS_TACTICAL.text", "PersonalityQuirk.ALWAYS_TACTICAL.description"), + BATTLE_SCREAM("PersonalityQuirk.BATTLE_SCREAM.text", "PersonalityQuirk.BATTLE_SCREAM.description"), + BRIEF_AND_TO_THE_POINT("PersonalityQuirk.BRIEF_AND_TO_THE_POINT.text", "PersonalityQuirk.BRIEF_AND_TO_THE_POINT.description"), + CALLSIGN_COLLECTOR("PersonalityQuirk.CALLSIGN_COLLECTOR.text", "PersonalityQuirk.CALLSIGN_COLLECTOR.description"), + CHATTERBOX("PersonalityQuirk.CHATTERBOX.text", "PersonalityQuirk.CHATTERBOX.description"), + COMBAT_ARTIST("PersonalityQuirk.COMBAT_ARTIST.text", "PersonalityQuirk.COMBAT_ARTIST.description"), + DARING_ESCAPE("PersonalityQuirk.DARING_ESCAPE.text", "PersonalityQuirk.DARING_ESCAPE.description"), + DOOMSDAY_PREPPER("PersonalityQuirk.DOOMSDAY_PREPPER.text", "PersonalityQuirk.DOOMSDAY_PREPPER.description"), + EQUIPMENT_SCAVENGER("PersonalityQuirk.EQUIPMENT_SCAVENGER.text", "PersonalityQuirk.EQUIPMENT_SCAVENGER.description"), + FRIEND_TO_FOES("PersonalityQuirk.FRIEND_TO_FOES.text", "PersonalityQuirk.FRIEND_TO_FOES.description"), + GUNG_HO("PersonalityQuirk.GUNG_HO.text", "PersonalityQuirk.GUNG_HO.description"), + INSPIRATIONAL_POET("PersonalityQuirk.INSPIRATIONAL_POET.text", "PersonalityQuirk.INSPIRATIONAL_POET.description"), + MEK_MATCHMAKER("PersonalityQuirk.MEK_MATCHMAKER.text", "PersonalityQuirk.MEK_MATCHMAKER.description"), + MISSILE_JUNKIE("PersonalityQuirk.MISSILE_JUNKIE.text", "PersonalityQuirk.MISSILE_JUNKIE.description"), + NEVER_RETREAT("PersonalityQuirk.NEVER_RETREAT.text", "PersonalityQuirk.NEVER_RETREAT.description"), + OPTIMISTIC_TO_A_FAULT("PersonalityQuirk.OPTIMISTIC_TO_A_FAULT.text", "PersonalityQuirk.OPTIMISTIC_TO_A_FAULT.description"), + REACTIVE("PersonalityQuirk.REACTIVE.text", "PersonalityQuirk.REACTIVE.description"), + RISK_TAKER("PersonalityQuirk.RISK_TAKER.text", "PersonalityQuirk.RISK_TAKER.description"), + SIGNATURE_MOVE("PersonalityQuirk.SIGNATURE_MOVE.text", "PersonalityQuirk.SIGNATURE_MOVE.description"), + TACTICAL_WITHDRAWAL("PersonalityQuirk.TACTICAL_WITHDRAWAL.text", "PersonalityQuirk.TACTICAL_WITHDRAWAL.description"), + ACCENT_SWITCHER("PersonalityQuirk.ACCENT_SWITCHER.text", "PersonalityQuirk.ACCENT_SWITCHER.description"), + AMBUSH_LOVER("PersonalityQuirk.AMBUSH_LOVER.text", "PersonalityQuirk.AMBUSH_LOVER.description"), + BATTLE_HARDENED("PersonalityQuirk.BATTLE_HARDENED.text", "PersonalityQuirk.BATTLE_HARDENED.description"), + BREAKS_RADIO_SILENCE("PersonalityQuirk.BREAKS_RADIO_SILENCE.text", "PersonalityQuirk.BREAKS_RADIO_SILENCE.description"), + CONVOY_LOVER("PersonalityQuirk.CONVOY_LOVER.text", "PersonalityQuirk.CONVOY_LOVER.description"), + DEBRIS_SLINGER("PersonalityQuirk.DEBRIS_SLINGER.text", "PersonalityQuirk.DEBRIS_SLINGER.description"), + CAMOUFLAGE("PersonalityQuirk.CAMOUFLAGE.text", "PersonalityQuirk.CAMOUFLAGE.description"), + DISTANT_LEADER("PersonalityQuirk.DISTANT_LEADER.text", "PersonalityQuirk.DISTANT_LEADER.description"), + DRAMATIC_FINISH("PersonalityQuirk.DRAMATIC_FINISH.text", "PersonalityQuirk.DRAMATIC_FINISH.description"), + ENGINE_REVERER("PersonalityQuirk.ENGINE_REVERER.text", "PersonalityQuirk.ENGINE_REVERER.description"), + FLIRTY_COMMS("PersonalityQuirk.FLIRTY_COMMS.text", "PersonalityQuirk.FLIRTY_COMMS.description"), + FOCUS_FREAK("PersonalityQuirk.FOCUS_FREAK.text", "PersonalityQuirk.FOCUS_FREAK.description"), + FOUL_MOUTHED("PersonalityQuirk.FOUL_MOUTHED.text", "PersonalityQuirk.FOUL_MOUTHED.description"), + FREESTYLE_COMBAT("PersonalityQuirk.FREESTYLE_COMBAT.text", "PersonalityQuirk.FREESTYLE_COMBAT.description"), + GEOMETRY_GURU("PersonalityQuirk.GEOMETRY_GURU.text", "PersonalityQuirk.GEOMETRY_GURU.description"), + ICE_COLD("PersonalityQuirk.ICE_COLD.text", "PersonalityQuirk.ICE_COLD.description"), + PICKY_ABOUT_GEAR("PersonalityQuirk.PICKY_ABOUT_GEAR.text", "PersonalityQuirk.PICKY_ABOUT_GEAR.description"), + RECORD_KEEPER("PersonalityQuirk.RECORD_KEEPER.text", "PersonalityQuirk.RECORD_KEEPER.description"), + RESOURCE_SCROUNGER("PersonalityQuirk.RESOURCE_SCROUNGER.text", "PersonalityQuirk.RESOURCE_SCROUNGER.description"), + TRASH_TALKER("PersonalityQuirk.TRASH_TALKER.text", "PersonalityQuirk.TRASH_TALKER.description"), + CORRECTS_PRONOUNS("PersonalityQuirk.CORRECTS_PRONOUNS.text", "PersonalityQuirk.CORRECTS_PRONOUNS.description"), + BODY_DISCOMFORT("PersonalityQuirk.BODY_DISCOMFORT.text", "PersonalityQuirk.BODY_DISCOMFORT.description"); // endregion Enum Declarations // region Variable Declarations @@ -158,406 +270,6 @@ public String getDescription() { public boolean isNone() { return this == NONE; } - - public boolean isAdjustsClothes() { - return this == ADJUSTS_CLOTHES; - } - - public boolean isAffectionate() { - return this == AFFECTIONATE; - } - - public boolean isApologetic() { - return this == APOLOGETIC; - } - - public boolean isBookworm() { - return this == BOOKWORM; - } - - public boolean isCalendar() { - return this == CALENDAR; - } - - public boolean isCandles() { - return this == CANDLES; - } - - public boolean isChewingGum() { - return this == CHEWING_GUM; - } - - public boolean isChronicLateness() { - return this == CHRONIC_LATENESS; - } - - public boolean isCleaner() { - return this == CLEANER; - } - - public boolean isCollector() { - return this == COLLECTOR; - } - - public boolean isCompetitiveNature() { - return this == COMPETITIVE_NATURE; - } - - public boolean isCompliments() { - return this == COMPLIMENTS; - } - - public boolean isDaydreamer() { - return this == DAYDREAMER; - } - - public boolean isDoodler() { - return this == DOODLER; - } - - public boolean isDoolittle() { - return this == DOOLITTLE; - } - - public boolean isDramatic() { - return this == DRAMATIC; - } - - public boolean isEatingHabits() { - return this == EATING_HABITS; - } - - public boolean isEnvironmentalSensitivity() { - return this == ENVIRONMENTAL_SENSITIVITY; - } - - public boolean isExcessiveCaution() { - return this == EXCESSIVE_CAUTION; - } - - public boolean isExcessiveGreeting() { - return this == EXCESSIVE_GREETING; - } - - public boolean isEyeContact() { - return this == EYE_CONTACT; - } - - public boolean isFashionChoices() { - return this == FASHION_CHOICES; - } - - public boolean isFidgets() { - return this == FIDGETS; - } - - public boolean isFitness() { - return this == FITNESS; - } - - public boolean isFixates() { - return this == FIXATES; - } - - public boolean isFlask() { - return this == FLASK; - } - - public boolean isFootTapper() { - return this == FOOT_TAPPER; - } - - public boolean isForgetful() { - return this == FORGETFUL; - } - - public boolean isFormalSpeech() { - return this == FORMAL_SPEECH; - } - - public boolean isFurniture() { - return this == FURNITURE; - } - - public boolean isGlasses() { - return this == GLASSES; - } - - public boolean isGloves() { - return this == GLOVES; - } - - public boolean isHandGestures() { - return this == HAND_GESTURES; - } - - public boolean isHandWringer() { - return this == HAND_WRINGER; - } - - public boolean isHandshake() { - return this == HANDSHAKE; - } - - public boolean isHeadphones() { - return this == HEADPHONES; - } - - public boolean isHealthySnacks() { - return this == HEALTHY_SNACKS; - } - - public boolean isHistorian() { - return this == HISTORIAN; - } - - public boolean isHummer() { - return this == HUMMER; - } - - public boolean isHygienic() { - return this == HYGIENIC; - } - - public boolean isIrregularSleeper() { - return this == IRREGULAR_SLEEPER; - } - - public boolean isJoker() { - return this == JOKER; - } - - public boolean isLists() { - return this == LISTS; - } - - public boolean isLiteral() { - return this == LITERAL; - } - - public boolean isLocks() { - return this == LOCKS; - } - - public boolean isMeasuredTalker() { - return this == MEASURED_TALKER; - } - - public boolean isMinimalist() { - return this == MINIMALIST; - } - - public boolean isMug() { - return this == MUG; - } - - public boolean isNailBiter() { - return this == NAIL_BITER; - } - - public boolean isNicknames() { - return this == NICKNAMING; - } - - public boolean isNightOwl() { - return this == NIGHT_OWL; - } - - public boolean isNoteTaker() { - return this == NOTE_TAKER; - } - - public boolean isNotebook() { - return this == NOTEBOOK; - } - - public boolean isObject() { - return this == OBJECT; - } - - public boolean isOrganizationalTendencies() { - return this == ORGANIZATIONAL_TENDENCIES; - } - - public boolean isOrganizer() { - return this == ORGANIZER; - } - - public boolean isOrigami() { - return this == ORIGAMI; - } - - public boolean isOverPlanner() { - return this == OVER_PLANNER; - } - - public boolean isOverExplainer() { - return this == OVEREXPLAINER; - } - - public boolean isPenClicker() { - return this == PEN_CLICKER; - } - - public boolean isPenTwirler() { - return this == PEN_TWIRLER; - } - - public boolean isPersonification() { - return this == PERSONIFICATION; - } - - public boolean isPessimist() { - return this == PESSIMIST; - } - - public boolean isPhrases() { - return this == PHRASES; - } - - public boolean isPlants() { - return this == PLANTS; - } - - public boolean isPolite() { - return this == POLITE; - } - - public boolean isPracticalJoker() { - return this == PRACTICAL_JOKER; - } - - public boolean isPrepared() { - return this == PREPARED; - } - - public boolean isPunctual() { - return this == PUNCTUAL; - } - - public boolean isPuzzles() { - return this == PUZZLES; - } - - public boolean isQuotes() { - return this == QUOTES; - } - - public boolean isRarelySleeps() { - return this == RARELY_SLEEPS; - } - - public boolean isRoutine() { - return this == ROUTINE; - } - - public boolean isSeeksApproval() { - return this == SEEKS_APPROVAL; - } - - public boolean isSentimental() { - return this == SENTIMENTAL; - } - - public boolean isSharpening() { - return this == SHARPENING; - } - - public boolean isSings() { - return this == SINGS; - } - - public boolean isSkeptical() { - return this == SKEPTICAL; - } - - public boolean isSleepTalker() { - return this == SLEEP_TALKER; - } - - public boolean isSmiler() { - return this == SMILER; - } - - public boolean isSnacks() { - return this == SNACKS; - } - - public boolean iStoryteller() { - return this == STORYTELLING; - } - - public boolean isStretching() { - return this == STRETCHING; - } - - public boolean isSuperstitiousRituals() { - return this == SUPERSTITIOUS_RITUALS; - } - - public boolean isSupervisedHabits() { - return this == SUPERVISED_HABITS; - } - - public boolean isTechTalk() { - return this == TECH_TALK; - } - - public boolean isTechnophobia() { - return this == TECHNOPHOBIA; - } - - public boolean isThesaurus() { - return this == THESAURUS; - } - - public boolean isThirdPerson() { - return this == THIRD_PERSON; - } - - public boolean isTimeManagement() { - return this == TIME_MANAGEMENT; - } - - public boolean isTinkerer() { - return this == TINKERER; - } - - public boolean isTruthTeller() { - return this == TRUTH_TELLER; - } - - public boolean isUnnecessaryCaution() { - return this == UNNECESSARY_CAUTION; - } - - public boolean isUnpredictableSpeech() { - return this == UNPREDICTABLE_SPEECH; - } - - public boolean isUnusualHobbies() { - return this == UNUSUAL_HOBBIES; - } - - public boolean isWatch() { - return this == WATCH; - } - - public boolean isWeatherman() { - return this == WEATHERMAN; - } - - public boolean isWhistler() { - return this == WHISTLER; - } - - public boolean isWorrier() { - return this == WORRIER; - } - - public boolean isWriter() { - return this == WRITER; - } // endregion Boolean Comparison Methods // region File I/O @@ -570,7 +282,7 @@ public boolean isWriter() { * @throws IllegalStateException if the given string does not match any valid * Quirk */ - + @Deprecated public static PersonalityQuirk parseFromString(final String quirk) { return switch (quirk) { case "0", "none" -> NONE; @@ -674,6 +386,7 @@ public static PersonalityQuirk parseFromString(final String quirk) { case "98", "Frequent Whistler" -> WHISTLER; case "99", "Persistent Worrier" -> WORRIER; case "100", "Writes Everything Down" -> WRITER; + case "101", "Constantly Reminiscing" -> BATTLEFIELD_NOSTALGIA; default -> throw new IllegalStateException( "Unexpected value in mekhq/campaign/personnel/enums/randomEvents/personalities/PersonalityQuirk.java/parseFromString: " @@ -681,6 +394,26 @@ public static PersonalityQuirk parseFromString(final String quirk) { }; } + /** + * Returns the {@link PersonalityQuirk} associated with the given ordinal. + * + * @param ordinal the ordinal value of the {@link PersonalityQuirk} + * @return the {@link PersonalityQuirk} associated with the given ordinal, or default value + * {@code NONE} if not found + */ + public static PersonalityQuirk fromOrdinal(int ordinal) { + for (PersonalityQuirk quirk : values()) { + if (quirk.ordinal() == ordinal) { + return quirk; + } + } + + final MMLogger logger = MMLogger.create(PersonalityQuirk.class); + logger.error(String.format("Unknown PersonalityQuirk ordinal: %s - returning NONE.", ordinal)); + + return NONE; + } + @Override public String toString() { return name; diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Social.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Social.java index 7c7b69c9ae..0c958983fe 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Social.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Social.java @@ -18,10 +18,11 @@ */ package mekhq.campaign.personnel.randomEvents.enums.personalities; -import java.util.ResourceBundle; - +import megamek.logging.MMLogger; import mekhq.MekHQ; +import java.util.ResourceBundle; + public enum Social { // region Enum Declarations NONE("Personality.NONE.text", "Personality.NONE.description", false, false), @@ -101,130 +102,9 @@ public boolean isTraitMajor() { // endregion Getters // region Boolean Comparison Methods - public boolean isNone() { return this == NONE; } - - public boolean isAltruistic() { - return this == ALTRUISTIC; - } - - public boolean isApathetic() { - return this == APATHETIC; - } - - public boolean isAuthentic() { - return this == AUTHENTIC; - } - - public boolean isBlunt() { - return this == BLUNT; - } - - public boolean isCallous() { - return this == CALLOUS; - } - - public boolean isCompassionate() { - return this == COMPASSIONATE; - } - - public boolean isCondescending() { - return this == CONDESCENDING; - } - - public boolean isConsiderate() { - return this == CONSIDERATE; - } - - public boolean isDisingenuous() { - return this == DISINGENUOUS; - } - - public boolean isDismissive() { - return this == DISMISSIVE; - } - - public boolean isEncouraging() { - return this == ENCOURAGING; - } - - public boolean isErratic() { - return this == ERRATIC; - } - - public boolean isEmpathetic() { - return this == EMPATHETIC; - } - - public boolean isFriendly() { - return this == FRIENDLY; - } - - public boolean isGregarious() { - return this == GREGARIOUS; - } - - public boolean isInspiring() { - return this == INSPIRING; - } - - public boolean isIndifferent() { - return this == INDIFFERENT; - } - - public boolean isIntroverted() { - return this == INTROVERTED; - } - - public boolean isIrritable() { - return this == IRRITABLE; - } - - public boolean isNarcissistic() { - return this == NARCISSISTIC; - } - - public boolean isNeglectful() { - return this == NEGLECTFUL; - } - - public boolean isPompous() { - return this == POMPOUS; - } - - public boolean isPetty() { - return this == PETTY; - } - - public boolean isPersuasive() { - return this == PERSUASIVE; - } - - public boolean isReceptive() { - return this == RECEPTIVE; - } - - public boolean isScheming() { - return this == SCHEMING; - } - - public boolean isSincere() { - return this == SINCERE; - } - - public boolean isSupportive() { - return this == SUPPORTIVE; - } - - public boolean isTactful() { - return this == TACTFUL; - } - - public boolean isUntrustworthy() { - return this == UNTRUSTWORTHY; - } // endregion Boolean Comparison Methods // region File I/O @@ -237,7 +117,7 @@ public boolean isUntrustworthy() { * @throws IllegalStateException if the given string does not match any valid * Social */ - + @Deprecated public static Social parseFromString(final String social) { return switch (social) { case "0", "None" -> NONE; @@ -281,54 +161,23 @@ public static Social parseFromString(final String social) { } /** - * Parses an integer value into an Social enum. + * Returns the {@link Social} associated with the given ordinal. * - * @param social the integer value representing the Social level - * @return the corresponding Social enum value - * @throws IllegalStateException if the integer value does not correspond to any - * valid Social enum value + * @param ordinal the ordinal value of the {@link Social} + * @return the {@link Social} associated with the given ordinal, or default value + * {@code NONE} if not found */ + public static Social fromOrdinal(int ordinal) { + for (Social social : values()) { + if (social.ordinal() == ordinal) { + return social; + } + } - public static Social parseFromInt(final int social) { - return switch (social) { - case 0 -> NONE; - // Minor Characteristics - case 1 -> APATHETIC; - case 2 -> AUTHENTIC; - case 3 -> BLUNT; - case 4 -> CALLOUS; - case 5 -> CONDESCENDING; - case 6 -> CONSIDERATE; - case 7 -> DISINGENUOUS; - case 8 -> DISMISSIVE; - case 9 -> ENCOURAGING; - case 10 -> ERRATIC; - case 11 -> EMPATHETIC; - case 12 -> FRIENDLY; - case 13 -> INSPIRING; - case 14 -> INDIFFERENT; - case 15 -> INTROVERTED; - case 16 -> IRRITABLE; - case 17 -> NEGLECTFUL; - case 18 -> PETTY; - case 19 -> PERSUASIVE; - case 20 -> RECEPTIVE; - case 21 -> SINCERE; - case 22 -> SUPPORTIVE; - case 23 -> TACTFUL; - case 24 -> UNTRUSTWORTHY; - // Major Characteristics - case 25 -> ALTRUISTIC; - case 26 -> COMPASSIONATE; - case 27 -> GREGARIOUS; - case 28 -> NARCISSISTIC; - case 29 -> POMPOUS; - case 30 -> SCHEMING; - default -> - throw new IllegalStateException( - "Unexpected value in mekhq/campaign/personnel/enums/randomEvents/personalities/Social.java/parseFromInt: " - + social); - }; + final MMLogger logger = MMLogger.create(Social.class); + logger.error(String.format("Unknown Social ordinal: %s - returning NONE.", ordinal)); + + return NONE; } @Override From 8168f6f20cd3e1e4954987048c0476592a0e2fdb Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 18:47:44 -0500 Subject: [PATCH 088/118] Remove redundant personality comparison methods Removed multiple redundant boolean comparison methods from Aggression, Ambition, and Greed enums. The simplification reduces code bloat and enhances maintainability by avoiding unnecessary duplication. --- .../enums/personalities/Aggression.java | 120 ----------------- .../enums/personalities/Ambition.java | 121 ------------------ .../enums/personalities/Greed.java | 121 ------------------ 3 files changed, 362 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java index 71240f83ac..296b994f75 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Aggression.java @@ -107,126 +107,6 @@ public boolean isTraitMajor() { public boolean isNone() { return this == NONE; } - - public boolean isBloodthirsty() { - return this == BLOODTHIRSTY; - } - - public boolean isBold() { - return this == BOLD; - } - - public boolean isAggressive() { - return this == AGGRESSIVE; - } - - public boolean isAssertive() { - return this == ASSERTIVE; - } - - public boolean isBelligerent() { - return this == BELLIGERENT; - } - - public boolean isBrash() { - return this == BRASH; - } - - public boolean isConfident() { - return this == CONFIDENT; - } - - public boolean isCourageous() { - return this == COURAGEOUS; - } - - public boolean isDaring() { - return this == DARING; - } - - public boolean isDecisive() { - return this == DECISIVE; - } - - public boolean isDetermined() { - return this == DETERMINED; - } - - public boolean isDiplomatic() { - return this == DIPLOMATIC; - } - - public boolean isDomineering() { - return this == DOMINEERING; - } - - public boolean isFearless() { - return this == FEARLESS; - } - - public boolean isHostile() { - return this == HOSTILE; - } - - public boolean isHotHeaded() { - return this == HOT_HEADED; - } - - public boolean isImpetuous() { - return this == IMPETUOUS; - } - - public boolean isImpulsive() { - return this == IMPULSIVE; - } - - public boolean isInflexible() { - return this == INFLEXIBLE; - } - - public boolean isIntrepid() { - return this == INTREPID; - } - - public boolean isMurderous() { - return this == MURDEROUS; - } - - public boolean isOverbearing() { - return this == OVERBEARING; - } - - public boolean isPacifistic() { - return this == PACIFISTIC; - } - - public boolean isReckless() { - return this == RECKLESS; - } - - public boolean isResolute() { - return this == RESOLUTE; - } - - public boolean isSadistic() { - return this == SADISTIC; - } - - public boolean isSavage() { - return this == SAVAGE; - } - - public boolean isStubborn() { - return this == STUBBORN; - } - - public boolean isTenacious() { - return this == TENACIOUS; - } - - public boolean isVigilant() { - return this == VIGILANT; - } // endregion Boolean Comparison Methods // region File I/O diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java index 2b59f3a032..7f68f04fad 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Ambition.java @@ -102,130 +102,9 @@ public boolean isTraitMajor() { // endregion Getters // region Boolean Comparison Methods - public boolean isNone() { return this == NONE; } - - public boolean isAmbitious() { - return this == AMBITIOUS; - } - - public boolean isArrogant() { - return this == ARROGANT; - } - - public boolean isAspiring() { - return this == ASPIRING; - } - - public boolean isCalculating() { - return this == CALCULATING; - } - - public boolean isConniving() { - return this == CONNIVING; - } - - public boolean isControlling() { - return this == CONTROLLING; - } - - public boolean isCutthroat() { - return this == CUTTHROAT; - } - - public boolean isDishonest() { - return this == DISHONEST; - } - - public boolean isDiligent() { - return this == DILIGENT; - } - - public boolean isDriven() { - return this == DRIVEN; - } - - public boolean isEnergetic() { - return this == ENERGETIC; - } - - public boolean isExcessive() { - return this == EXCESSIVE; - } - - public boolean isFocused() { - return this == FOCUSED; - } - - public boolean isGoalOriented() { - return this == GOAL_ORIENTED; - } - - public boolean isInnovative() { - return this == INNOVATIVE; - } - - public boolean isManipulative() { - return this == MANIPULATIVE; - } - - public boolean isMotivated() { - return this == MOTIVATED; - } - - public boolean isOpportunistic() { - return this == OPPORTUNISTIC; - } - - public boolean isOverconfident() { - return this == OVERCONFIDENT; - } - - public boolean isPersistent() { - return this == PERSISTENT; - } - - public boolean isProactive() { - return this == PROACTIVE; - } - - public boolean isResilient() { - return this == RESILIENT; - } - - public boolean isResourceful() { - return this == RESOURCEFUL; - } - - public boolean isRuthless() { - return this == RUTHLESS; - } - - public boolean isSelfish() { - return this == SELFISH; - } - - public boolean isStrategic() { - return this == STRATEGIC; - } - - public boolean isTyrannical() { - return this == TYRANNICAL; - } - - public boolean isUnambitious() { - return this == UNAMBITIOUS; - } - - public boolean isUnscrupulous() { - return this == UNSCRUPULOUS; - } - - public boolean isVisionary() { - return this == VISIONARY; - } // endregion Boolean Comparison Methods // region File I/O diff --git a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java index e0e9f2f5ac..e63bc71c94 100644 --- a/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java +++ b/MekHQ/src/mekhq/campaign/personnel/randomEvents/enums/personalities/Greed.java @@ -102,130 +102,9 @@ public boolean isTraitMajor() { // endregion Getters // region Boolean Comparison Methods - public boolean isNone() { return this == NONE; } - - public boolean isAstute() { - return this == ASTUTE; - } - - public boolean isAdept() { - return this == ADEPT; - } - - public boolean isAvaricious() { - return this == AVARICIOUS; - } - - public boolean isCorrupt() { - return this == CORRUPT; - } - - public boolean isDynamic() { - return this == DYNAMIC; - } - - public boolean isEager() { - return this == EAGER; - } - - public boolean isEnterprising() { - return this == ENTERPRISING; - } - - public boolean isExploitative() { - return this == EXPLOITATIVE; - } - - public boolean isFraudulent() { - return this == FRAUDULENT; - } - - public boolean isGenerous() { - return this == GENEROUS; - } - - public boolean isGreedy() { - return this == GREEDY; - } - - public boolean isHoarding() { - return this == HOARDING; - } - - public boolean isInsatiable() { - return this == INSATIABLE; - } - - public boolean isInsightful() { - return this == INSIGHTFUL; - } - - public boolean isIntuitive() { - return this == INTUITIVE; - } - - public boolean isJudicious() { - return this == JUDICIOUS; - } - - public boolean isLustful() { - return this == LUSTFUL; - } - - public boolean isMercenary() { - return this == MERCENARY; - } - - public boolean isMeticulous() { - return this == METICULOUS; - } - - public boolean isNefarious() { - return this == NEFARIOUS; - } - - public boolean isOverreaching() { - return this == OVERREACHING; - } - - public boolean isProfitable() { - return this == PROFITABLE; - } - - public boolean isSavvy() { - return this == SAVVY; - } - - public boolean isSelfServing() { - return this == SELF_SERVING; - } - - public boolean isShameless() { - return this == SHAMELESS; - } - - public boolean isShrewd() { - return this == SHREWD; - } - - public boolean isTactical() { - return this == TACTICAL; - } - - public boolean isThief() { - return this == THIEF; - } - - public boolean isUnprincipled() { - return this == UNPRINCIPLED; - } - - public boolean isVoracious() { - return this == VORACIOUS; - } // endregion Boolean Comparison Methods // region File I/O From 0f8cd21eadc149ca2a995fc58dd345a6cd82af32 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 18:49:41 -0500 Subject: [PATCH 089/118] Add line continuation to long description properties Improved readability by adding line continuation characters in long description strings of personality traits in Personalities.properties. This change helps maintain clarity and manage lines within the 80-character limit. --- .../mekhq/resources/Personalities.properties | 1426 ++++++++++++++--- 1 file changed, 1182 insertions(+), 244 deletions(-) diff --git a/MekHQ/resources/mekhq/resources/Personalities.properties b/MekHQ/resources/mekhq/resources/Personalities.properties index 84c355370e..856dbf82f5 100644 --- a/MekHQ/resources/mekhq/resources/Personalities.properties +++ b/MekHQ/resources/mekhq/resources/Personalities.properties @@ -1,505 +1,1443 @@ # Default Personality.NONE.text=None Personality.NONE.description=ERROR: THIS SHOULDN'T BE VISIBLE! + Intelligence.AVERAGE.text=Average Intelligence.AVERAGE.description=ERROR: THIS SHOULDN'T BE VISIBLE! # Aggression Aggression.BLOODTHIRSTY.text=Bloodthirsty -Aggression.BLOODTHIRSTY.description=%s exhibit%s a relentless and savage desire for violence, never hesitating to engage in brutal conflict. +Aggression.BLOODTHIRSTY.description=%s exhibit%s a relentless and savage desire for violence, never\ + \ hesitating to engage in brutal conflict. + Aggression.BOLD.text=Bold -Aggression.BOLD.description=%s face%s challenges head-on with courage and confidence, unafraid to take risks or stand out. +Aggression.BOLD.description=%s face%s challenges head-on with courage and confidence, unafraid to take\ + \ risks or stand out. + Aggression.AGGRESSIVE.text=Aggressive -Aggression.AGGRESSIVE.description=%s consistently pursue%s goals with intense and forceful determination, often dominating situations with assertive behavior. +Aggression.AGGRESSIVE.description=%s consistently pursue%s goals with intense and forceful determination,\ + \ often dominating situations with assertive behavior. + Aggression.ASSERTIVE.text=Assertive -Aggression.ASSERTIVE.description=%s communicate%s confidently and directly, ensuring their needs and opinions are clearly understood without being overbearing. +Aggression.ASSERTIVE.description=%s communicate%s confidently and directly, ensuring their needs and\ + \ opinions are clearly understood without being overbearing. + Aggression.BELLIGERENT.text=Belligerent -Aggression.BELLIGERENT.description=%s frequently engage%s in confrontations and disputes, often displaying a combative and hostile attitude. +Aggression.BELLIGERENT.description=%s frequently engage%s in confrontations and disputes, often displaying\ + \ a combative and hostile attitude. + Aggression.BRASH.text=Brash -Aggression.BRASH.description=%s act%s with impulsive boldness, often making decisions without considering the consequences. +Aggression.BRASH.description=%s act%s with impulsive boldness, often making decisions without considering\ + \ the consequences. + Aggression.CONFIDENT.text=Confident -Aggression.CONFIDENT.description=%s hold%s an unwavering belief in their abilities, exuding self-assurance in every situation. +Aggression.CONFIDENT.description=%s hold%s an unwavering belief in their abilities, exuding self-assurance\ + \ in every situation. + Aggression.COURAGEOUS.text=Courageous -Aggression.COURAGEOUS.description=%s face%s danger and adversity with remarkable bravery and a steady resolve. +Aggression.COURAGEOUS.description=%s face%s danger and adversity with remarkable bravery and a steady\ + \ resolve. + Aggression.DARING.text=Daring -Aggression.DARING.description=%s embrace%s risky and adventurous endeavors with a bold and fearless spirit. +Aggression.DARING.description=%s embrace%s risky and adventurous endeavors with a bold and fearless\ + \ spirit. + Aggression.DECISIVE.text=Decisive -Aggression.DECISIVE.description=%s make%s clear and effective decisions swiftly, even under pressure, demonstrating strong judgment and confidence. +Aggression.DECISIVE.description=%s make%s clear and effective decisions swiftly, even under pressure,\ + \ demonstrating strong judgment and confidence. + Aggression.DETERMINED.text=Determined -Aggression.DETERMINED.description=%s pursue%s their goals with unwavering resolve and persistence, overcoming obstacles with steadfast dedication. +Aggression.DETERMINED.description=%s pursue%s their goals with unwavering resolve and persistence,\ + \ overcoming obstacles with steadfast dedication. + Aggression.DIPLOMATIC.text=Diplomatic -Aggression.DIPLOMATIC.description=%s navigate%s complex situations with tact and sensitivity, skillfully managing relationships and conflicts with a balanced approach. +Aggression.DIPLOMATIC.description=%s navigate%s complex situations with tact and sensitivity, skillfully\ + \ managing relationships and conflicts with a balanced approach. + Aggression.DOMINEERING.text=Domineering -Aggression.DOMINEERING.description=%s exert%s excessive control and influence over others, often imposing their will in a commanding and authoritative manner. +Aggression.DOMINEERING.description=%s exert%s excessive control and influence over others, often imposing\ + \ their will in a commanding and authoritative manner. + Aggression.FEARLESS.text=Fearless -Aggression.FEARLESS.description=%s confront%s every challenge with unwavering bravery, undeterred by potential dangers or risks. +Aggression.FEARLESS.description=%s confront%s every challenge with unwavering bravery, undeterred by\ + \ potential dangers or risks. + Aggression.HOSTILE.text=Hostile -Aggression.HOSTILE.description=%s consistently exhibit%s a negative and antagonistic attitude, often showing open antagonism or aggression towards others. +Aggression.HOSTILE.description=%s consistently exhibit%s a negative and antagonistic attitude, often\ + \ showing open antagonism or aggression towards others. + Aggression.HOT_HEADED.text=Hot-Headed -Aggression.HOT_HEADED.description=%s react%s impulsively and with intense emotion, often allowing anger or frustration to dictate their actions. +Aggression.HOT_HEADED.description=%s react%s impulsively and with intense emotion, often allowing anger\ + \ or frustration to dictate their actions. + Aggression.IMPETUOUS.text=Impetuous -Aggression.IMPETUOUS.description=%s act%s with sudden and passionate decisions, often without fully considering the consequences or potential risks. +Aggression.IMPETUOUS.description=%s act%s with sudden and passionate decisions, often without fully\ + \ considering the consequences or potential risks. + Aggression.IMPULSIVE.text=Impulsive -Aggression.IMPULSIVE.description=%s make%s spontaneous decisions and actions based on immediate desires or emotions, frequently without thorough planning or forethought. +Aggression.IMPULSIVE.description=%s make%s spontaneous decisions and actions based on immediate desires\ + \ or emotions, frequently without thorough planning or forethought. + Aggression.INFLEXIBLE.text=Inflexible -Aggression.INFLEXIBLE.description=%s stick%s rigidly to their views or methods, often resisting change or adaptation even in the face of new information or circumstances. +Aggression.INFLEXIBLE.description=%s stick%s rigidly to their views or methods, often resisting change\ + \ or adaptation even in the face of new information or circumstances. + Aggression.INTREPID.text=Intrepid -Aggression.INTREPID.description=%s face%s challenges and dangers with unwavering bravery and adventurous spirit, showing a fearless commitment to exploring the unknown. +Aggression.INTREPID.description=%s face%s challenges and dangers with unwavering bravery and adventurous\ + \ spirit, showing a fearless commitment to exploring the unknown. + Aggression.MURDEROUS.text=Murderous -Aggression.MURDEROUS.description=%s exhibit%s a chilling and relentless intent to cause harm or death, displaying a violent and ruthless demeanor. +Aggression.MURDEROUS.description=%s exhibit%s a chilling and relentless intent to cause harm or death,\ + \ displaying a violent and ruthless demeanor. + Aggression.OVERBEARING.text=Overbearing -Aggression.OVERBEARING.description=%s impose%s their will on others with excessive forcefulness, often disregarding others' opinions and preferences in favor of their own. +Aggression.OVERBEARING.description=%s impose%s their will on others with excessive forcefulness, often\ + \ disregarding others' opinions and preferences in favor of their own. + Aggression.PACIFISTIC.text=Pacifistic -Aggression.PACIFISTIC.description=%s consistently avoid%s conflict and strives for peaceful resolutions, prioritizing harmony and nonviolence in their interactions. +Aggression.PACIFISTIC.description=%s consistently avoid%s conflict and strives for peaceful resolutions,\ + \ prioritizing harmony and nonviolence in their interactions. + Aggression.RECKLESS.text=Reckless -Aggression.RECKLESS.description=%s act%s with a lack of caution or consideration for potential dangers, often taking significant risks without regard for the consequences. +Aggression.RECKLESS.description=%s act%s with a lack of caution or consideration for potential dangers,\ + \ often taking significant risks without regard for the consequences. + Aggression.RESOLUTE.text=Resolute -Aggression.RESOLUTE.description=%s remain%s steadfast and unwavering in their purpose or decision, showing strong determination and commitment despite challenges. +Aggression.RESOLUTE.description=%s remain%s steadfast and unwavering in their purpose or decision,\ + \ showing strong determination and commitment despite challenges. + Aggression.SADISTIC.text=Sadistic -Aggression.SADISTIC.description=%s derive%s pleasure from inflicting pain or suffering on others, exhibiting cruel and malicious enjoyment of others' distress. +Aggression.SADISTIC.description=%s derive%s pleasure from inflicting pain or suffering on others,\ + \ exhibiting cruel and malicious enjoyment of others' distress. + Aggression.SAVAGE.text=Savage -Aggression.SAVAGE.description=%s display%s a brutal and ferocious nature, engaging in actions or behavior with ruthless aggression and a lack of compassion. +Aggression.SAVAGE.description=%s display%s a brutal and ferocious nature, engaging in actions or\ + \ behavior with ruthless aggression and a lack of compassion. + Aggression.STUBBORN.text=Stubborn -Aggression.STUBBORN.description=%s adhere%s firmly to their own beliefs or decisions, often resisting change or compromise despite strong arguments or evidence. +Aggression.STUBBORN.description=%s adhere%s firmly to their own beliefs or decisions, often resisting\ + \ change or compromise despite strong arguments or evidence. + Aggression.TENACIOUS.text=Tenacious -Aggression.TENACIOUS.description=%s cling%s resolutely to their goals and endeavors, showing relentless perseverance and determination in the face of obstacles. +Aggression.TENACIOUS.description=%s cling%s resolutely to their goals and endeavors, showing relentless\ + \ perseverance and determination in the face of obstacles. + Aggression.VIGILANT.text=Vigilant -Aggression.VIGILANT.description=%s remain%s constantly alert and watchful, carefully monitoring their surroundings and potential threats to ensure safety and readiness. +Aggression.VIGILANT.description=%s remain%s constantly alert and watchful, carefully monitoring their\ + \ surroundings and potential threats to ensure safety and readiness. # Ambition Ambition.AMBITIOUS.text=Ambitious -Ambition.AMBITIOUS.description=%s demonstrate%s a strong desire to achieve great things and advance, driven by a clear vision and determination to reach high goals. +Ambition.AMBITIOUS.description=%s demonstrate%s a strong desire to achieve great things and advance,\ + \ driven by a clear vision and determination to reach high goals. + Ambition.ARROGANT.text=Arrogant -Ambition.ARROGANT.description=%s exhibit%s an inflated sense of self-importance and superiority, often dismissing others' opinions and capabilities with condescension. +Ambition.ARROGANT.description=%s exhibit%s an inflated sense of self-importance and superiority, often\ + \ dismissing others' opinions and capabilities with condescension. + Ambition.ASPIRING.text=Aspiring -Ambition.ASPIRING.description=%s strive%s to achieve their goals and reach new heights, consistently striving towards personal or professional growth. +Ambition.ASPIRING.description=%s strive%s to achieve their goals and reach new heights, consistently\ + \ striving towards personal or professional growth. + Ambition.CALCULATING.text=Calculating -Ambition.CALCULATING.description=%s confront%s situations with careful and strategic thought, often making decisions based on thorough analysis and expecting potential outcomes. +Ambition.CALCULATING.description=%s confront%s situations with careful and strategic thought, often\ + \ making decisions based on thorough analysis and expecting potential outcomes. + Ambition.CONNIVING.text=Conniving -Ambition.CONNIVING.description=%s engage%s in secretive and deceitful behavior to manipulate situations and achieve their own ends, often at the expense of others. +Ambition.CONNIVING.description=%s engage%s in secretive and deceitful behavior to manipulate situations\ + \ and achieve their own ends, often at the expense of others. + Ambition.CONTROLLING.text=Controlling -Ambition.CONTROLLING.description=%s exert%s a dominant influence over people and situations, often seeking to dictate or manage every detail to maintain power and authority. +Ambition.CONTROLLING.description=%s exert%s a dominant influence over people and situations, often\ + \ seeking to dictate or manage every detail to maintain power and authority. + Ambition.CUTTHROAT.text=Cutthroat -Ambition.CUTTHROAT.description=%s pursue%s their goals with ruthless determination, often disregarding ethics and using aggressive tactics to outmaneuver and defeat others. +Ambition.CUTTHROAT.description=%s pursue%s their goals with ruthless determination, often disregarding\ + \ ethics and using aggressive tactics to outmaneuver and defeat others. + Ambition.DISHONEST.text=Dishonest -Ambition.DISHONEST.description=%s frequently misrepresent%s the truth or engages in deceitful behavior, often to manipulate others or gain an unfair advantage. +Ambition.DISHONEST.description=%s frequently misrepresent%s the truth or engages in deceitful behavior,\ + \ often to manipulate others or gain an unfair advantage. + Ambition.DILIGENT.text=Diligent -Ambition.DILIGENT.description=%s consistently show%s thorough effort and careful attention to their work, demonstrating a strong commitment to completing tasks with precision and reliability. +Ambition.DILIGENT.description=%s consistently show%s thorough effort and careful attention to their work,\ + \ demonstrating a strong commitment to completing tasks with precision and reliability. + Ambition.DRIVEN.text=Driven -Ambition.DRIVEN.description=%s demonstrate%s a strong inner motivation and determination, relentlessly pursuing their goals and ambitions with focused energy and persistence. +Ambition.DRIVEN.description=%s demonstrate%s a strong inner motivation and determination, relentlessly\ + \ pursuing their goals and ambitions with focused energy and persistence. + Ambition.ENERGETIC.text=Energetic -Ambition.ENERGETIC.description=%s tackle%s tasks and activities with high levels of enthusiasm and vitality, bringing a lively and dynamic presence to everything they do. +Ambition.ENERGETIC.description=%s tackle%s tasks and activities with high levels of enthusiasm and vitality,\ + \ bringing a lively and dynamic presence to everything they do. + Ambition.EXCESSIVE.text=Excessive -Ambition.EXCESSIVE.description=%s often show%s over-the-top behavior or indulgence that exceeds what is necessary or appropriate. +Ambition.EXCESSIVE.description=%s often show%s over-the-top behavior or indulgence that exceeds what\ + \ is necessary or appropriate. + Ambition.FOCUSED.text=Focused -Ambition.FOCUSED.description=%s maintain%s a clear and determined concentration on their goals, effectively directing their attention and efforts to achieve specific goals without distraction. +Ambition.FOCUSED.description=%s maintain%s a clear and determined concentration on their goals, effectively\ + \ directing their attention and efforts to achieve specific goals without distraction. + Ambition.GOAL_ORIENTED.text=Goal-Oriented -Ambition.GOAL_ORIENTED.description=%s direct%s their efforts and energy towards achieving specific goals, consistently prioritizing and working towards defined targets with purpose and determination. +Ambition.GOAL_ORIENTED.description=%s direct%s their efforts and energy towards achieving specific goals,\ + \ consistently prioritizing and working towards defined targets with purpose and determination. + Ambition.INNOVATIVE.text=Innovative -Ambition.INNOVATIVE.description=%s consistently introduce%s new ideas and approaches, demonstrating creativity and originality in solving problems and driving progress. +Ambition.INNOVATIVE.description=%s consistently introduce%s new ideas and approaches, demonstrating\ + \ creativity and originality in solving problems and driving progress. + Ambition.MANIPULATIVE.text=Manipulative -Ambition.MANIPULATIVE.description=%s skillfully influence%s and controls others through cunning or deceitful tactics, often to achieve their own ends or gain an advantage. +Ambition.MANIPULATIVE.description=%s skillfully influence%s and controls others through cunning or\ + \ deceitful tactics, often to achieve their own ends or gain an advantage. + Ambition.MOTIVATED.text=Motivated -Ambition.MOTIVATED.description=%s pride%s themselves on a strong sense of purpose and enthusiasm, actively pursuing their goals with energy and commitment. +Ambition.MOTIVATED.description=%s pride%s themselves on a strong sense of purpose and enthusiasm,\ + \ actively pursuing their goals with energy and commitment. + Ambition.OPPORTUNISTIC.text=Opportunistic -Ambition.OPPORTUNISTIC.description=%s take%s advantage of favorable circumstances or situations to advance their own interests, often capitalizing on opportunities as they arise without regard for long-term consequences. +Ambition.OPPORTUNISTIC.description=%s take%s advantage of favorable circumstances or situations to\ + \ advance their own interests, often capitalizing on opportunities as they arise without regard for\ + \ long-term consequences. + Ambition.OVERCONFIDENT.text=Overconfident -Ambition.OVERCONFIDENT.description=%s display%s an excessive sense of self-assurance, often underestimating risks and overestimating their own abilities or knowledge. +Ambition.OVERCONFIDENT.description=%s display%s an excessive sense of self-assurance, often underestimating\ + \ risks and overestimating their own abilities or knowledge. + Ambition.PERSISTENT.text=Persistent -Ambition.PERSISTENT.description=%s continue%s to pursue their goals with unwavering determination, refusing to give up despite challenges or setbacks. +Ambition.PERSISTENT.description=%s continue%s to pursue their goals with unwavering determination,\ + \ refusing to give up despite challenges or setbacks. + Ambition.PROACTIVE.text=Proactive -Ambition.PROACTIVE.description=%s take%s initiative and anticipates potential issues, actively making plans and taking action to address them before they arise. +Ambition.PROACTIVE.description=%s take%s initiative and anticipates potential issues, actively making\ + \ plans and taking action to address them before they arise. + Ambition.RESILIENT.text=Resilient -Ambition.RESILIENT.description=%s recover%s quickly from setbacks and adapts well to adversity, demonstrating the ability to withstand challenges and bounce back stronger. +Ambition.RESILIENT.description=%s recover%s quickly from setbacks and adapts well to adversity,\ + \ demonstrating the ability to withstand challenges and bounce back stronger. + Ambition.RESOURCEFUL.text=Resourceful -Ambition.RESOURCEFUL.description=%s effectively utilize%s available resources and creative problem-solving skills to overcome obstacles and achieve goals, often finding innovative solutions with limited means. +Ambition.RESOURCEFUL.description=%s effectively utilize%s available resources and creative problem-solving\ + \ skills to overcome obstacles and achieve goals, often finding innovative solutions with limited means. + Ambition.RUTHLESS.text=Ruthless -Ambition.RUTHLESS.description=%s pursue%s their goals with a lack of compassion or regard for others, often making harsh decisions and taking aggressive actions without a concern for the consequences. +Ambition.RUTHLESS.description=%s pursue%s their goals with a lack of compassion or regard for others,\ + \ often making harsh decisions and taking aggressive actions without a concern for the consequences. + Ambition.SELFISH.text=Selfish -Ambition.SELFISH.description=%s prioritize%s their own needs and desires above others, often disregarding the impact of their actions on those around them. +Ambition.SELFISH.description=%s prioritize%s their own needs and desires above others, often disregarding\ + \ the impact of their actions on those around them. + Ambition.STRATEGIC.text=Strategic -Ambition.STRATEGIC.description=%s plan%s and executes actions with careful consideration of long-term goals and potential outcomes, using thoughtful and calculated approaches to achieve success. +Ambition.STRATEGIC.description=%s plan%s and executes actions with careful consideration of long-term\ + \ goals and potential outcomes, using thoughtful and calculated approaches to achieve success. + Ambition.TYRANNICAL.text=Tyrannical -Ambition.TYRANNICAL.description=%s rule%s with absolute power and oppression, often imposing harsh and authoritarian control over others with little regard for their freedoms or well-being. +Ambition.TYRANNICAL.description=%s rule%s with absolute power and oppression, often imposing harsh and\ + \ authoritarian control over others with little regard for their freedoms or well-being. + Ambition.UNAMBITIOUS.text=Unambitious -Ambition.UNAMBITIOUS.description=%s lack%s strong motivation or desire to achieve significant goals, showing little interest in pursuing advancement or improvement. +Ambition.UNAMBITIOUS.description=%s lack%s strong motivation or desire to achieve significant goals,\ + \ showing little interest in pursuing advancement or improvement. + Ambition.UNSCRUPULOUS.text=Unscrupulous -Ambition.UNSCRUPULOUS.description=%s engage%s in actions without moral principles or ethical considerations, often using deceit or unethical methods to achieve their ends. +Ambition.UNSCRUPULOUS.description=%s engage%s in actions without moral principles or ethical considerations,\ + \ often using deceit or unethical methods to achieve their ends. + Ambition.VISIONARY.text=Visionary -Ambition.VISIONARY.description=%s show%s a clear and imaginative foresight of future possibilities, often inspiring and guiding others with innovative ideas and a long-term perspective. +Ambition.VISIONARY.description=%s show%s a clear and imaginative foresight of future possibilities,\ + \ often inspiring and guiding others with innovative ideas and a long-term perspective. # Greed Greed.ASTUTE.text=Astute -Greed.ASTUTE.description=%s display%s sharp intelligence and perceptiveness, skillfully understanding and navigating complex situations with keen insight and strategic thinking. +Greed.ASTUTE.description=%s display%s sharp intelligence and perceptiveness, skillfully understanding\ + \ and navigating complex situations with keen insight and strategic thinking. + Greed.ADEPT.text=Adept -Greed.ADEPT.description=%s demonstrate%s exceptional skill and proficiency in their field, effectively handling tasks with expertise and competence. +Greed.ADEPT.description=%s demonstrate%s exceptional skill and proficiency in their field, effectively\ + \ handling tasks with expertise and competence. + Greed.AVARICIOUS.text=Avaricious -Greed.AVARICIOUS.description=%s exhibit%s an insatiable and greedy desire for wealth or material gain, often prioritizing their own financial interests over ethical considerations. +Greed.AVARICIOUS.description=%s exhibit%s an insatiable and greedy desire for wealth or material gain,\ + \ often prioritizing their own financial interests over ethical considerations. + Greed.CORRUPT.text=Corrupt -Greed.CORRUPT.description=%s engage%s in unethical or illegal activities, often exploiting their position or power for personal gain at the expense of integrity and fairness. +Greed.CORRUPT.description=%s engage%s in unethical or illegal activities, often exploiting their\ + \ position or power for personal gain at the expense of integrity and fairness. + Greed.DYNAMIC.text=Dynamic -Greed.DYNAMIC.description=%s demonstrate%s constant change and energy, bringing a vibrant and adaptable approach to their work and interactions, often thriving in evolving environments. +Greed.DYNAMIC.description=%s demonstrate%s constant change and energy, bringing a vibrant and adaptable\ + \ approach to their work and interactions, often thriving in evolving environments. + Greed.EAGER.text=Eager -Greed.EAGER.description=%s confront%s tasks and opportunities with enthusiastic anticipation and a strong desire to get involved and make progress. +Greed.EAGER.description=%s confront%s tasks and opportunities with enthusiastic anticipation and a\ + \ strong desire to get involved and make progress. + Greed.ENTERPRISING.text=Enterprising -Greed.ENTERPRISING.description=%s display%s a proactive and inventive approach to identifying and pursuing new opportunities, often taking initiative to start projects and ventures with creativity and resourcefulness. +Greed.ENTERPRISING.description=%s display%s a proactive and inventive approach to identifying and\ + \ pursuing new opportunities, often taking initiative to start projects and ventures with creativity\ + \ and resourcefulness. + Greed.EXPLOITATIVE.text=Exploitative -Greed.EXPLOITATIVE.description=%s take%s advantage of others' vulnerabilities or resources for personal gain, often prioritizing their own benefit over fairness and ethical considerations. +Greed.EXPLOITATIVE.description=%s take%s advantage of others' vulnerabilities or resources for personal\ + \ gain, often prioritizing their own benefit over fairness and ethical considerations. + Greed.FRAUDULENT.text=Fraudulent -Greed.FRAUDULENT.description=%s engage%s in deceitful practices or misrepresentations, often with the intent to deceive others and gain an unfair advantage or benefit. +Greed.FRAUDULENT.description=%s engage%s in deceitful practices or misrepresentations, often with the\ + \ intent to deceive others and gain an unfair advantage or benefit. + Greed.GENEROUS.text=Generous -Greed.GENEROUS.description=%s show%s a willingness to give and share freely, often extending kindness and support to others without expecting anything in return. +Greed.GENEROUS.description=%s show%s a willingness to give and share freely, often extending kindness\ + \ and support to others without expecting anything in return. + Greed.GREEDY.text=Greedy -Greed.GREEDY.description=%s demonstrat%s an excessive and selfish desire for more wealth, possessions, or power, often prioritizing their own accumulation over the needs or well-being of others. +Greed.GREEDY.description=%s demonstrat%s an excessive and selfish desire for more wealth, possessions,\ + \ or power, often prioritizing their own accumulation over the needs or well-being of others. + Greed.HOARDING.text=Hoarding -Greed.HOARDING.description=%s accumulate%s and retains excessive amounts of resources or possessions, often due to a fear of scarcity or a desire for control, while rarely sharing or using them. +Greed.HOARDING.description=%s accumulate%s and retains excessive amounts of resources or possessions,\ + \ often due to a fear of scarcity or a desire for control, while rarely sharing or using them. + Greed.INSATIABLE.text=Insatiable -Greed.INSATIABLE.description=%s show%s an unquenchable and relentless desire for something, continually seeking more and never feeling satisfied or fulfilled. +Greed.INSATIABLE.description=%s show%s an unquenchable and relentless desire for something, continually\ + \ seeking more and never feeling satisfied or fulfilled. + Greed.INSIGHTFUL.text=Insightful -Greed.INSIGHTFUL.description=%s demonstrate%s deep understanding and perceptiveness, providing valuable and clear perspectives on complex situations or ideas. +Greed.INSIGHTFUL.description=%s demonstrate%s deep understanding and perceptiveness, providing valuable\ + \ and clear perspectives on complex situations or ideas. + Greed.INTUITIVE.text=Intuitive -Greed.INTUITIVE.description=%s use%s their instincts and gut feelings to understand and respond to situations, often grasping concepts or making decisions without needing extensive reasoning. +Greed.INTUITIVE.description=%s use%s their instincts and gut feelings to understand and respond to\ + \ situations, often grasping concepts or making decisions without needing extensive reasoning. + Greed.JUDICIOUS.text=Judicious -Greed.JUDICIOUS.description=%s make%s decisions with careful and balanced consideration, using wisdom and sound judgment to evaluate options and potential outcomes. +Greed.JUDICIOUS.description=%s make%s decisions with careful and balanced consideration, using wisdom\ + \ and sound judgment to evaluate options and potential outcomes. + Greed.LUSTFUL.text=Lustful -Greed.LUSTFUL.description=%s exhibit%s an intense and uncontrolled desire for sexual pleasure or gratification, often prioritizing physical attraction and desire. +Greed.LUSTFUL.description=%s exhibit%s an intense and uncontrolled desire for sexual pleasure or\ + \ gratification, often prioritizing physical attraction and desire. + Greed.MERCENARY.text=Mercenary -Greed.MERCENARY.description=%s find%s motivation in financial gain or personal advantage, often prioritizing profit over loyalty or ethical considerations. +Greed.MERCENARY.description=%s find%s motivation in financial gain or personal advantage, often \ + prioritizing profit over loyalty or ethical considerations. + Greed.METICULOUS.text=Meticulous -Greed.METICULOUS.description=%s show%s great attention to detail and precision, carefully ensuring that every aspect of a task or project is executed with accuracy and thoroughness. +Greed.METICULOUS.description=%s show%s great attention to detail and precision, carefully ensuring\ + \ that every aspect of a task or project is executed with accuracy and thoroughness. + Greed.NEFARIOUS.text=Nefarious -Greed.NEFARIOUS.description=%s engage%s in wicked or morally reprehensible actions, often driven by malevolent intentions and a disregard for ethical standards. +Greed.NEFARIOUS.description=%s engage%s in wicked or morally reprehensible actions, often driven by\ + \ malevolent intentions and a disregard for ethical standards. + Greed.OVERREACHING.text=Overreaching -Greed.OVERREACHING.description=%s exceed%s reasonable limits in their ambitions or actions, often attempting to achieve more than is practical or achievable, sometimes leading to negative consequences. +Greed.OVERREACHING.description=%s exceed%s reasonable limits in their ambitions or actions, often\ + \ attempting to achieve more than is practical or achievable, sometimes leading to negative consequences. + Greed.PROFITABLE.text=Profitable -Greed.PROFITABLE.description=%s generate%s significant financial gain or benefit, effectively leveraging resources or opportunities to achieve significant economic success. +Greed.PROFITABLE.description=%s generate%s significant financial gain or benefit, effectively leveraging\ + \ resources or opportunities to achieve significant economic success. + Greed.SAVVY.text=Savvy -Greed.SAVVY.description=%s demonstrate%s practical knowledge and shrewdness, skillfully navigating situations with a clear understanding and strategic approach. +Greed.SAVVY.description=%s demonstrate%s practical knowledge and shrewdness, skillfully navigating\ + \ situations with a clear understanding and strategic approach. + Greed.SELF_SERVING.text=Self-Serving -Greed.SELF_SERVING.description=%s prioritize%s their own interests and benefits, often making decisions and taking actions that advance their personal agenda, sometimes at the expense of others. +Greed.SELF_SERVING.description=%s prioritize%s their own interests and benefits, often making decisions\ + \ and taking actions that advance their personal agenda, sometimes at the expense of others. + Greed.SHAMELESS.text=Shameless -Greed.SHAMELESS.description=%s exhibit%s a blatant disregard for social norms or moral standards, acting with boldness and without any sense of embarrassment or guilt. +Greed.SHAMELESS.description=%s exhibit%s a blatant disregard for social norms or moral standards,\ + \ acting with boldness and without any sense of embarrassment or guilt. + Greed.SHREWD.text=Shrewd -Greed.SHREWD.description=%s hold%s keen judgment and practical intelligence, making astute decisions and navigating complex situations with strategic insight and cunning. +Greed.SHREWD.description=%s hold%s keen judgment and practical intelligence, making astute decisions\ + \ and navigating complex situations with strategic insight and cunning. + Greed.TACTICAL.text=Tactical -Greed.TACTICAL.description=%s employ%s careful planning and strategic maneuvers to achieve specific short-term goals, focusing on immediate actions to support broader goals. +Greed.TACTICAL.description=%s employ%s careful planning and strategic maneuvers to achieve specific\ + \ short-term goals, focusing on immediate actions to support broader goals. + Greed.THIEF.text=Thief -Greed.THIEF.description=%s engage%s in the act of stealing, unlawfully taking others' possessions or property with the intent to benefit themselves at the expense of others. +Greed.THIEF.description=%s engage%s in the act of stealing, unlawfully taking others' possessions or\ + \ property with the intent to benefit themselves at the expense of others. + Greed.UNPRINCIPLED.text=Unprincipled -Greed.UNPRINCIPLED.description=%s lack%s a strong moral code or ethical standards, often engaging in actions or decisions without regard for right or wrong. +Greed.UNPRINCIPLED.description=%s lack%s a strong moral code or ethical standards, often engaging in\ + \ actions or decisions without regard for right or wrong. + Greed.VORACIOUS.text=Voracious -Greed.VORACIOUS.description=%s display%s an intense and eager appetite for something, whether it's food, knowledge, or an activity, often consuming or pursuing it with great enthusiasm and intensity. +Greed.VORACIOUS.description=%s display%s an intense and eager appetite for something, whether it's\ + \ food, knowledge, or an activity, often consuming or pursuing it with great enthusiasm and intensity. # Social Social.ALTRUISTIC.text=Altruistic -Social.ALTRUISTIC.description=%s act%s with genuine concern for the well-being of others, selflessly providing help and support without expecting anything in return. +Social.ALTRUISTIC.description=%s act%s with genuine concern for the well-being of others, selflessly\ + \ providing help and support without expecting anything in return. + Social.APATHETIC.text=Apathetic -Social.APATHETIC.description=%s show%s a lack of interest or concern, demonstrating indifference towards situations or issues that typically would elicit a response or emotion. +Social.APATHETIC.description=%s show%s a lack of interest or concern, demonstrating indifference towards\ + \ situations or issues that typically would elicit a response or emotion. + Social.AUTHENTIC.text=Authentic -Social.AUTHENTIC.description=%s consistently align%s their actions and words with their true values and beliefs, without pretense or deceit. +Social.AUTHENTIC.description=%s consistently align%s their actions and words with their true values\ + \ and beliefs, without pretense or deceit. + Social.BLUNT.text=Blunt -Social.BLUNT.description=%s communicate%s in a direct and straightforward manner, often prioritizing honesty over tact, which can come across as abrupt or harsh. +Social.BLUNT.description=%s communicate%s in a direct and straightforward manner, often prioritizing\ + \ honesty over tact, which can come across as abrupt or harsh. + Social.CALLOUS.text=Callous -Social.CALLOUS.description=%s exhibit%s a lack of empathy or sensitivity, often showing a hardened and indifferent attitude towards others' feelings or suffering. +Social.CALLOUS.description=%s exhibit%s a lack of empathy or sensitivity, often showing a hardened\ + \ and indifferent attitude towards others' feelings or suffering. + Social.COMPASSIONATE.text=Compassionate -Social.COMPASSIONATE.description=%s show%s deep empathy and concern for others, consistently offering kindness and support to those in need. +Social.COMPASSIONATE.description=%s show%s deep empathy and concern for others, consistently offering\ + \ kindness and support to those in need. + Social.CONDESCENDING.text=Condescending -Social.CONDESCENDING.description=%s behave%s in a patronizing manner, treating others as though they're inferior or less important, often with a sense of superiority. +Social.CONDESCENDING.description=%s behave%s in a patronizing manner, treating others as though they're\ + \ inferior or less important, often with a sense of superiority. + Social.CONSIDERATE.text=Considerate -Social.CONSIDERATE.description=%s show%s consideration and is always mindful of others' needs and feelings, taking care to act in ways that show respect and concern for their well-being. +Social.CONSIDERATE.description=%s show%s consideration and is always mindful of others' needs and feelings,\ + \ taking care to act in ways that show respect and concern for their well-being. + Social.DISINGENUOUS.text=Disingenuous -Social.DISINGENUOUS.description=%s present%s a facade of sincerity while concealing their true intentions or feelings, often engaging in deceitful or manipulative behavior. +Social.DISINGENUOUS.description=%s present%s a facade of sincerity while concealing their true intentions\ + \ or feelings, often engaging in deceitful or manipulative behavior. + Social.DISMISSIVE.text=Dismissive -Social.DISMISSIVE.description=%s treat%s others' opinions or concerns with disregard or lack of respect, often brushing them off as unimportant or irrelevant. +Social.DISMISSIVE.description=%s treat%s others' opinions or concerns with disregard or lack of respect,\ + \ often brushing them off as unimportant or irrelevant. + Social.ENCOURAGING.text=Encouraging -Social.ENCOURAGING.description=%s provide%s support and positive reinforcement, motivating others and fostering confidence through uplifting and reassuring words or actions. +Social.ENCOURAGING.description=%s provide%s support and positive reinforcement, motivating others and\ + \ fostering confidence through uplifting and reassuring words or actions. + Social.ERRATIC.text=Erratic -Social.ERRATIC.description=%s exhibit%s unpredictable and inconsistent behavior, often shifting abruptly in actions or moods without a clear pattern or reason. +Social.ERRATIC.description=%s exhibit%s unpredictable and inconsistent behavior, often shifting abruptly\ + \ in actions or moods without a clear pattern or reason. + Social.EMPATHETIC.text=Empathetic -Social.EMPATHETIC.description=%s deeply understand%s and shares the feelings of others, responding with genuine care and concern to their emotions and experiences. +Social.EMPATHETIC.description=%s deeply understand%s and shares the feelings of others, responding\ + \ with genuine care and concern to their emotions and experiences. + Social.FRIENDLY.text=Friendly -Social.FRIENDLY.description=%s interact%s with others in a warm and approachable manner, creating a welcoming and pleasant atmosphere through kindness and openness. +Social.FRIENDLY.description=%s interact%s with others in a warm and approachable manner, creating a\ + \ welcoming and pleasant atmosphere through kindness and openness. + Social.GREGARIOUS.text=Gregarious -Social.GREGARIOUS.description=%s enjoy%s being around others and is highly sociable, thriving in social settings and often seeking out opportunities to connect and interact with people. +Social.GREGARIOUS.description=%s enjoy%s being around others and is highly sociable, thriving in social\ + \ settings and often seeking out opportunities to connect and interact with people. + Social.INSPIRING.text=Inspiring -Social.INSPIRING.description=%s motivate%s and uplifts others through their actions, words, or achievements, encouraging them to pursue their own goals and reach their full potential. +Social.INSPIRING.description=%s motivate%s and uplifts others through their actions, words, or achievements,\ + \ encouraging them to pursue their own goals and reach their full potential. + Social.INDIFFERENT.text=Indifferent -Social.INDIFFERENT.description=%s show%s a lack of interest or concern, displaying emotional detachment and often ignoring or dismissing situations or people. +Social.INDIFFERENT.description=%s show%s a lack of interest or concern, displaying emotional detachment\ + \ and often ignoring or dismissing situations or people. + Social.INTROVERTED.text=Introverted -Social.INTROVERTED.description=%s prefer%s solitude or small, close-knit groups over large social settings, often finding energy and comfort in introspection and personal space. +Social.INTROVERTED.description=%s prefer%s solitude or small, close-knit groups over large social settings,\ + \ often finding energy and comfort in introspection and personal space. + Social.IRRITABLE.text=Irritable -Social.IRRITABLE.description=%s often react%s with impatience or agitation to minor inconveniences or disruptions. +Social.IRRITABLE.description=%s often react%s with impatience or agitation to minor inconveniences or\ + \ disruptions. + Social.NARCISSISTIC.text=Narcissistic -Social.NARCISSISTIC.description=%s display%s an excessive sense of self-importance and a strong need for admiration, often showing a lack of empathy and a preoccupation with their own interests and desires. +Social.NARCISSISTIC.description=%s display%s an excessive sense of self-importance and a strong need\ + \ for admiration, often showing a lack of empathy and a preoccupation with their own interests and\ + \ desires. + Social.NEGLECTFUL.text=Neglectful -Social.NEGLECTFUL.description=%s fail%s to give adequate attention or care to important responsibilities or relationships, often overlooking or disregarding tasks or the needs of others. +Social.NEGLECTFUL.description=%s fail%s to give adequate attention or care to important responsibilities\ + \ or relationships, often overlooking or disregarding tasks or the needs of others. + Social.POMPOUS.text=Pompous -Social.POMPOUS.description=%s display%s an exaggerated sense of self-importance and grandeur, often acting in a boastful and self-satisfied manner. +Social.POMPOUS.description=%s display%s an exaggerated sense of self-importance and grandeur, often\ + \ acting in a boastful and self-satisfied manner. + Social.PETTY.text=Petty -Social.PETTY.description=%s fixate%s on trivial or insignificant matters, often allowing minor issues to cause unnecessary conflict or irritation. +Social.PETTY.description=%s fixate%s on trivial or insignificant matters, often allowing minor issues\ + \ to cause unnecessary conflict or irritation. + Social.PERSUASIVE.text=Persuasive -Social.PERSUASIVE.description=%s effectively convince%s others to adopt their views or take specific actions, using compelling arguments and charm to sway opinions. +Social.PERSUASIVE.description=%s effectively convince%s others to adopt their views or take specific\ + \ actions, using compelling arguments and charm to sway opinions. + Social.RECEPTIVE.text=Receptive -Social.RECEPTIVE.description=%s show%s a willingness to listen and consider different perspectives, they're open and responsive to new ideas, feedback, or experiences, showing a willingness to listen and consider different perspectives.. +Social.RECEPTIVE.description=%s show%s a willingness to listen and consider different perspectives,\ + \ they're open and responsive to new ideas, feedback, or experiences, showing a willingness to listen\ + \ and consider different perspectives. + Social.SCHEMING.text=Scheming -Social.SCHEMING.description=%s engage%s in secretive and strategic planning, often devising intricate plans to achieve personal goals or manipulate situations, sometimes with deceitful intent. +Social.SCHEMING.description=%s engage%s in secretive and strategic planning, often devising intricate\ + \ plans to achieve personal goals or manipulate situations, sometimes with deceitful intent. + Social.SINCERE.text=Sincere -Social.SINCERE.description=%s show%s genuine feelings and intentions, acting with honesty and authenticity in their interactions with others. +Social.SINCERE.description=%s show%s genuine feelings and intentions, acting with honesty and authenticity\ + \ in their interactions with others. + Social.SUPPORTIVE.text=Supportive -Social.SUPPORTIVE.description=%s offer%s encouragement and assistance, providing help and reassurance to others in a way that fosters their well-being and success. +Social.SUPPORTIVE.description=%s offer%s encouragement and assistance, providing help and reassurance\ + \ to others in a way that fosters their well-being and success. + Social.TACTFUL.text=Tactful -Social.TACTFUL.description=%s communicate%s with sensitivity and discretion, skillfully handling delicate situations and addressing others with care and consideration. +Social.TACTFUL.description=%s communicate%s with sensitivity and discretion, skillfully handling delicate\ + \ situations and addressing others with care and consideration. + Social.UNTRUSTWORTHY.text=Untrustworthy -Social.UNTRUSTWORTHY.description=%s frequently betray%s confidence or fails to follow through on promises, showing a pattern of unreliable or deceitful behavior. +Social.UNTRUSTWORTHY.description=%s frequently betray%s confidence or fails to follow through on promises,\ + \ showing a pattern of unreliable or deceitful behavior. # Intelligence Intelligence.BRAIN_DEAD.text=Brain Dead -Intelligence.BRAIN_DEAD.description=%s show%s an extreme lack of intellectual ability or comprehension, struggling with even the most fundamental tasks and concepts, often appearing completely disengaged from cognitive processes. +Intelligence.BRAIN_DEAD.description=%s show%s an extreme lack of intellectual ability or comprehension,\ + \ struggling with even the most fundamental tasks and concepts, often appearing completely disengaged\ + \ from cognitive processes. + Intelligence.UNINTELLIGENT.text=Unintelligent -Intelligence.UNINTELLIGENT.description=%s consistently show%s a lack of understanding and difficulty grasping even basic concepts, often appearing confused or slow in their thinking. +Intelligence.UNINTELLIGENT.description=%s consistently show%s a lack of understanding and difficulty\ + \ grasping even basic concepts, often appearing confused or slow in their thinking. + Intelligence.FOOLISH.text=Foolish -Intelligence.FOOLISH.description=%s exhibit%s a marked lack of intellectual capacity and mental agility, finding it challenging to grasp fundamental ideas or reason effectively. +Intelligence.FOOLISH.description=%s exhibit%s a marked lack of intellectual capacity and mental agility,\ + \ finding it challenging to grasp fundamental ideas or reason effectively. + Intelligence.SIMPLE.text=Simple -Intelligence.SIMPLE.description=%s live%s a straightforward and uncomplicated life, often finding contentment in modest and basic experiences without seeking complexity or extravagance. +Intelligence.SIMPLE.description=%s live%s a straightforward and uncomplicated life, often finding\ + \ contentment in modest and basic experiences without seeking complexity or extravagance. + Intelligence.SLOW.text=Slow -Intelligence.SLOW.description=%s take%s extra time to grasp concepts or ideas, often struggling to understand or process information quickly. +Intelligence.SLOW.description=%s take%s extra time to grasp concepts or ideas, often struggling to\ + \ understand or process information quickly. + Intelligence.UNINSPIRED.text=Uninspired -Intelligence.UNINSPIRED.description=%s lack%s creativity or quick thinking, often sticking to basic or conventional approaches without demonstrating deeper insight or originality. +Intelligence.UNINSPIRED.description=%s lack%s creativity or quick thinking, often sticking to basic\ + \ or conventional approaches without demonstrating deeper insight or originality. + Intelligence.DULL.text=Dull -Intelligence.DULL.description=%s show%s a lack of quick understanding or insight, often struggling to grasp concepts or respond promptly in conversations or situations. +Intelligence.DULL.description=%s show%s a lack of quick understanding or insight, often struggling\ + \ to grasp concepts or respond promptly in conversations or situations. + Intelligence.DIMWITTED.text=Dimwitted -Intelligence.DIMWITTED.description=%s often find%s it difficult to keep up with fast-paced discussions or quickly adapt to new ideas, showing a slower rate of learning and comprehension. +Intelligence.DIMWITTED.description=%s often find%s it difficult to keep up with fast-paced discussions\ + \ or quickly adapt to new ideas, showing a slower rate of learning and comprehension. + Intelligence.OBTUSE.text=Obtuse -Intelligence.OBTUSE.description=%s may have trouble understanding intricate concepts or making connections between ideas, often needing more time to grasp even basic information. +Intelligence.OBTUSE.description=%s may have trouble understanding intricate concepts or making\ + \ connections between ideas, often needing more time to grasp even basic information. + Intelligence.BELOW_AVERAGE.text=Below Average -Intelligence.BELOW_AVERAGE.description=%s demonstrate%s cognitive abilities that aren't as developed as those of their peers, often finding it challenging to grasp complex concepts or solve problems quickly. +Intelligence.BELOW_AVERAGE.description=%s demonstrate%s cognitive abilities that aren't as developed\ + \ as those of their peers, often finding it challenging to grasp complex concepts or solve problems\ + \ quickly. + Intelligence.UNDER_PERFORMING.text=Under Performing -Intelligence.UNDER_PERFORMING.description=%s struggle%s to meet typical expectations or standards, often finding it challenging to solve problems or understand concepts that others grasp more easily. +Intelligence.UNDER_PERFORMING.description=%s struggle%s to meet typical expectations or standards,\ + \ often finding it challenging to solve problems or understand concepts that others grasp more easily. + Intelligence.LIMITED_INSIGHT.text=Limited Insight -Intelligence.LIMITED_INSIGHT.description=%s show%s difficulty grasping more complex or abstract ideas, showing a tendency to rely on simpler explanations or solutions. +Intelligence.LIMITED_INSIGHT.description=%s show%s difficulty grasping more complex or abstract ideas,\ + \ showing a tendency to rely on simpler explanations or solutions. + Intelligence.ABOVE_AVERAGE.text=Above Average -Intelligence.ABOVE_AVERAGE.description=%s demonstrate%s a higher level of cognitive ability than most, often excelling in understanding complex concepts and solving problems efficiently. +Intelligence.ABOVE_AVERAGE.description=%s demonstrate%s a higher level of cognitive ability than most,\ + \ often excelling in understanding complex concepts and solving problems efficiently. + Intelligence.STUDIOUS.text=Studious -Intelligence.STUDIOUS.description=%s quickly perceive%s and understands complex ideas and situations, demonstrating sharp insight and keen judgment in their observations and decisions. +Intelligence.STUDIOUS.description=%s quickly perceive%s and understands complex ideas and situations,\ + \ demonstrating sharp insight and keen judgment in their observations and decisions. + Intelligence.DISCERNING.text=Discerning -Intelligence.DISCERNING.description=%s offer%s profound understanding and perspectives, often providing deep analysis and innovative solutions to challenging problems. +Intelligence.DISCERNING.description=%s offer%s profound understanding and perspectives, often providing\ + \ deep analysis and innovative solutions to challenging problems. + Intelligence.SHARP.text=Sharp -Intelligence.SHARP.description=%s display%s quick and keen intelligence, easily understanding and responding to complex situations with insightful and clever thinking. +Intelligence.SHARP.description=%s display%s quick and keen intelligence, easily understanding and\ + \ responding to complex situations with insightful and clever thinking. + Intelligence.QUICK_WITTED.text=Quick-Witted -Intelligence.QUICK_WITTED.description=%s respond%s with rapid and clever thinking, often delivering insightful or humorous remarks with impressive speed and accuracy. +Intelligence.QUICK_WITTED.description=%s respond%s with rapid and clever thinking, often delivering\ + \ insightful or humorous remarks with impressive speed and accuracy. + Intelligence.PERCEPTIVE.text=Perceptive -Intelligence.PERCEPTIVE.description=%s demonstrate%s an acute awareness of their surroundings and can swiftly grasp underlying meanings or implications, showing a keen ability to understand subtle details. +Intelligence.PERCEPTIVE.description=%s demonstrate%s an acute awareness of their surroundings and can\ + \ swiftly grasp underlying meanings or implications, showing a keen ability to understand subtle details. + Intelligence.BRIGHT.text=Bright -Intelligence.BRIGHT.description=%s show%s a high level of intelligence and quick understanding, often excelling in academic or intellectual pursuits. +Intelligence.BRIGHT.description=%s show%s a high level of intelligence and quick understanding, often\ + \ excelling in academic or intellectual pursuits. + Intelligence.CLEVER.text=Clever -Intelligence.CLEVER.description=%s display%s an ability to solve problems and understand concepts quickly and ingeniously, often finding creative and effective solutions. +Intelligence.CLEVER.description=%s display%s an ability to solve problems and understand concepts\ + \ quickly and ingeniously, often finding creative and effective solutions. + Intelligence.INTELLECTUAL.text=Intellectual -Intelligence.INTELLECTUAL.description=%s show%s a natural and quick grasp of complex ideas or situations, often understanding things effortlessly and accurately without extensive explanation. +Intelligence.INTELLECTUAL.description=%s show%s a natural and quick grasp of complex ideas or situations,\ + \ often understanding things effortlessly and accurately without extensive explanation. + Intelligence.BRILLIANT.text=Brilliant -Intelligence.BRILLIANT.description=%s exhibit%s exceptional intellectual ability and creativity, often coming up with innovative solutions and grasping complex concepts with ease. +Intelligence.BRILLIANT.description=%s exhibit%s exceptional intellectual ability and creativity, often\ + \ coming up with innovative solutions and grasping complex concepts with ease. + Intelligence.EXCEPTIONAL.text=Exceptional -Intelligence.EXCEPTIONAL.description=%s demonstrate%s extraordinary intelligence and creativity, consistently producing innovative ideas and solutions that surpass typical standards. +Intelligence.EXCEPTIONAL.description=%s demonstrate%s extraordinary intelligence and creativity,\ + \ consistently producing innovative ideas and solutions that surpass typical standards. + Intelligence.GENIUS.text=Genius -Intelligence.GENIUS.description=%s exhibit%s an extraordinary level of intellect and insight, often making groundbreaking contributions or achieving remarkable understanding in their field of expertise. +Intelligence.GENIUS.description=%s exhibit%s an extraordinary level of intellect and insight, often\ + \ making groundbreaking contributions or achieving remarkable understanding in their field of expertise. # Quirks PersonalityQuirk.ADJUSTS_CLOTHES.text=Constantly Adjusting Clothes -PersonalityQuirk.ADJUSTS_CLOTHES.description=%s always fidget%s with their clothing, adjusting collars, cuffs, or hems compulsively. +PersonalityQuirk.ADJUSTS_CLOTHES.description=%s always fidget%s with their clothing, adjusting collars,\ + \ cuffs, or hems compulsively. + PersonalityQuirk.AFFECTIONATE.text=Overly Affectionate -PersonalityQuirk.AFFECTIONATE.description=%s frequently hug%s others, often invading personal space in the process. +PersonalityQuirk.AFFECTIONATE.description=%s frequently hug%s others, often invading personal space\ + \ in the process. + PersonalityQuirk.APOLOGETIC.text=Overly Apologetic -PersonalityQuirk.APOLOGETIC.description=%s frequently apologize%s for even the smallest mistakes or inconveniences, often unnecessarily. +PersonalityQuirk.APOLOGETIC.description=%s frequently apologize%s for even the smallest mistakes or\ + \ inconveniences, often unnecessarily. + PersonalityQuirk.BOOKWORM.text=Always Reading -PersonalityQuirk.BOOKWORM.description=%s tote%s a book or e-reader everywhere, reading at every opportunity, even in short breaks. +PersonalityQuirk.BOOKWORM.description=%s tote%s a book or e-reader everywhere, reading at every\ + \ opportunity, even in short breaks. + PersonalityQuirk.CALENDAR.text=Keeps a Personal Calendar -PersonalityQuirk.CALENDAR.description=%s maintains a personal calendar with detailed notes on daily tasks and upcoming events, checking it regularly. +PersonalityQuirk.CALENDAR.description=%s maintains a personal calendar with detailed notes on daily\ + \ tasks and upcoming events, checking it regularly. + PersonalityQuirk.CANDLES.text=Fond of Scented Candles -PersonalityQuirk.CANDLES.description=%s love%s scented candles and always has one burning in their personal space, creating a distinct aroma. +PersonalityQuirk.CANDLES.description=%s love%s scented candles and always has one burning in their\ + \ personal space, creating a distinct aroma. + PersonalityQuirk.CHEWING_GUM.text=Always Chewing Gum -PersonalityQuirk.CHEWING_GUM.description=%s always chew%s a piece of gum in their mouth and offers gum to others constantly. +PersonalityQuirk.CHEWING_GUM.description=%s always chew%s a piece of gum in their mouth and offers\ + \ gum to others constantly. + PersonalityQuirk.CHRONIC_LATENESS.text=Chronically Late -PersonalityQuirk.CHRONIC_LATENESS.description=%s almost always run%s late to meetings or events, no matter how much they plan ahead. +PersonalityQuirk.CHRONIC_LATENESS.description=%s almost always run%s late to meetings or events, no\ + \ matter how much they plan ahead. + PersonalityQuirk.CLEANER.text=Compulsive Cleaner -PersonalityQuirk.CLEANER.description=%s often clean%s things that are already clean, constantly tidying up their surroundings. +PersonalityQuirk.CLEANER.description=%s often clean%s things that are already clean, constantly tidying\ + \ up their surroundings. + PersonalityQuirk.COLLECTOR.text=Collects Odd Items -PersonalityQuirk.COLLECTOR.description=%s collect%s unusual or quirky items, like bottle caps, vintage toys or miniature BattleMeks, displaying them proudly. +PersonalityQuirk.COLLECTOR.description=%s collect%s unusual or quirky items, like bottle caps, vintage\ + \ toys or miniature BattleMeks, displaying them proudly. + PersonalityQuirk.COMPETITIVE_NATURE.text=Competitive Nature -PersonalityQuirk.COMPETITIVE_NATURE.description=%s alway%s turns every situation into a competition, whether it's a friendly game or a simple task, and dislikes losing or being bested. +PersonalityQuirk.COMPETITIVE_NATURE.description=%s alway%s turns every situation into a competition,\ + \ whether it's a friendly game or a simple task, and dislikes losing or being bested. + PersonalityQuirk.COMPLIMENTS.text=Excessive Complimenter -PersonalityQuirk.COMPLIMENTS.description=%s frequently compliment%s others, sometimes to the point of being insincere or annoying. +PersonalityQuirk.COMPLIMENTS.description=%s frequently compliment%s others, sometimes to the point\ + \ of being insincere or annoying. + PersonalityQuirk.DAYDREAMER.text=Prone to Daydreaming -PersonalityQuirk.DAYDREAMER.description=%s frequently drift%s off into daydreams, sometimes losing track of conversations or tasks at hand. +PersonalityQuirk.DAYDREAMER.description=%s frequently drift%s off into daydreams, sometimes losing\ + \ track of conversations or tasks at hand. + PersonalityQuirk.DOODLER.text=Compulsive Doodler -PersonalityQuirk.DOODLER.description=%s doodle%s on any available surface when they have a pen in hand, including important documents. +PersonalityQuirk.DOODLER.description=%s doodle%s on any available surface when they have a pen in\ + \ hand, including important documents. + PersonalityQuirk.DOOLITTLE.text=Talks to Animals -PersonalityQuirk.DOOLITTLE.description=%s show%s a habit of talking to animals as if they were people, often having full conversations with them. +PersonalityQuirk.DOOLITTLE.description=%s show%s a habit of talking to animals as if they were people,\ + \ often having full conversations with them. + PersonalityQuirk.DRAMATIC.text=Overly Dramatic -PersonalityQuirk.DRAMATIC.description=%s tend%s to react to situations with exaggerated emotions, turning minor issues into big dramas. +PersonalityQuirk.DRAMATIC.description=%s tend%s to react to situations with exaggerated emotions,\ + \ turning minor issues into big dramas. + PersonalityQuirk.EATING_HABITS.text=Unpredictable Eating Habits -PersonalityQuirk.EATING_HABITS.description=%s demonstrate%s very peculiar eating habits, such as eating only specific foods in certain orders or at specific times. +PersonalityQuirk.EATING_HABITS.description=%s demonstrate%s very peculiar eating habits, such as eating\ + \ only specific foods in certain orders or at specific times. + PersonalityQuirk.ENVIRONMENTAL_SENSITIVITY.text=Extreme Environmental Sensitivity -PersonalityQuirk.ENVIRONMENTAL_SENSITIVITY.description=%s let%s environmental factors like temperature, humidity, or noise levels, affect their performance and mood. +PersonalityQuirk.ENVIRONMENTAL_SENSITIVITY.description=%s let%s environmental factors like temperature,\ + \ humidity, or noise levels, affect their performance and mood. + PersonalityQuirk.EXCESSIVE_CAUTION.text=Excessive Caution -PersonalityQuirk.EXCESSIVE_CAUTION.description=%s take%s extreme precautionary measures for even minor tasks or risks, like wearing multiple layers of protective gear or checking equipment obsessively. +PersonalityQuirk.EXCESSIVE_CAUTION.description=%s take%s extreme precautionary measures for even minor\ + \ tasks or risks, like wearing multiple layers of protective gear or checking equipment obsessively. + PersonalityQuirk.EXCESSIVE_GREETING.text=Over-the-Top Greetings -PersonalityQuirk.EXCESSIVE_GREETING.description=%s greet%s everyone with an elaborate and theatrical routine, complete with gestures or a signature phrase, making every interaction a little show. +PersonalityQuirk.EXCESSIVE_GREETING.description=%s greet%s everyone with an elaborate and theatrical\ + \ routine, complete with gestures or a signature phrase, making every interaction a little show. PersonalityQuirk.EYE_CONTACT.text=Intense Eye Contact -PersonalityQuirk.EYE_CONTACT.description=%s maintain%s intense eye contact during conversations, sometimes making others uncomfortable with their unwavering gaze. + +PersonalityQuirk.EYE_CONTACT.description=%s maintain%s intense eye contact during conversations, sometimes\ + \ making others uncomfortable with their unwavering gaze. + PersonalityQuirk.FASHION_CHOICES.text=Eccentric Fashion Choices -PersonalityQuirk.FASHION_CHOICES.description=%s wear%s clothing or accessories that are highly unconventional or mismatched, making a personal statement through their appearance. +PersonalityQuirk.FASHION_CHOICES.description=%s wear%s clothing or accessories that are highly\ + \ unconventional or mismatched, making a personal statement through their appearance. + PersonalityQuirk.FIDGETS.text=Constantly Fidgeting -PersonalityQuirk.FIDGETS.description=%s is alway%s fidgeting with something, whether it's a pen, a piece of equipment, or their own clothing, especially when nervous or bored. +PersonalityQuirk.FIDGETS.description=%s is alway%s fidgeting with something, whether it's a pen, a\ + \ piece of equipment, or their own clothing, especially when nervous or bored. + PersonalityQuirk.FITNESS.text=Extreme Personal Fitness Routine -PersonalityQuirk.FITNESS.description=%s follow%s a rigorous and unusual fitness routine, often working out at odd hours or in unconventional ways. +PersonalityQuirk.FITNESS.description=%s follow%s a rigorous and unusual fitness routine, often working\ + \ out at odd hours or in unconventional ways. + PersonalityQuirk.FIXATES.text=Fixates on One Topic -PersonalityQuirk.FIXATES.description=%s tend%s to talk about one subject obsessively, regardless of whether others are interested. +PersonalityQuirk.FIXATES.description=%s tend%s to talk about one subject obsessively, regardless of\ + \ whether others are interested. + PersonalityQuirk.FLASK.text=Carries a Flask -PersonalityQuirk.FLASK.description=%s always wear%s a flask of their favorite beverage, sipping from it frequently throughout the day. +PersonalityQuirk.FLASK.description=%s always wear%s a flask of their favorite beverage, sipping from\ + \ it frequently throughout the day. + PersonalityQuirk.FOOT_TAPPER.text=Always Tapping Foot -PersonalityQuirk.FOOT_TAPPER.description=%s constantly tap%s their foot, creating a steady rhythm that can sometimes distract others. +PersonalityQuirk.FOOT_TAPPER.description=%s constantly tap%s their foot, creating a steady rhythm\ + \ that can sometimes distract others. + PersonalityQuirk.FORGETFUL.text=Chronically Forgetful -PersonalityQuirk.FORGETFUL.description=%s often forget%s names, dates, and important details, relying heavily on notes and reminders. +PersonalityQuirk.FORGETFUL.description=%s often forget%s names, dates, and important details, relying\ + \ heavily on notes and reminders. + PersonalityQuirk.FORMAL_SPEECH.text=Overly Formal Speech -PersonalityQuirk.FORMAL_SPEECH.description=%s speak%s in an overly formal manner, using proper titles and full names, even in casual conversations. +PersonalityQuirk.FORMAL_SPEECH.description=%s speak%s in an overly formal manner, using proper titles\ + \ and full names, even in casual conversations. + PersonalityQuirk.FURNITURE.text=Constantly Rearranges Furniture -PersonalityQuirk.FURNITURE.description=%s frequently change%s the arrangement of furniture and personal items, seeking the perfect layout. +PersonalityQuirk.FURNITURE.description=%s frequently change%s the arrangement of furniture and personal\ + \ items, seeking the perfect layout. + PersonalityQuirk.GLASSES.text=Constantly Adjusts Glasses -PersonalityQuirk.GLASSES.description=%s continually adjust%s their glasses, even if they're perfectly in place. +PersonalityQuirk.GLASSES.description=%s continually adjust%s their glasses, even if they're perfectly\ + \ in place. + PersonalityQuirk.GLOVES.text=Always Wearing Gloves -PersonalityQuirk.GLOVES.description=%s always wear%s gloves, even in situations where they aren't necessary, considering it a personal style or comfort preference. +PersonalityQuirk.GLOVES.description=%s always wear%s gloves, even in situations where they aren't\ + \ necessary, considering it a personal style or comfort preference. + PersonalityQuirk.HAND_GESTURES.text=Excessive Hand Gestures -PersonalityQuirk.HAND_GESTURES.description=%s use%s large and dramatic hand gestures when speaking, often knocking things over or drawing unnecessary attention. +PersonalityQuirk.HAND_GESTURES.description=%s use%s large and dramatic hand gestures when speaking,\ + \ often knocking things over or drawing unnecessary attention. + PersonalityQuirk.HAND_WRINGER.text=Compulsive Hand-Wringer -PersonalityQuirk.HAND_WRINGER.description=%s wring%s their hands constantly, especially when nervous or deep in thought. +PersonalityQuirk.HAND_WRINGER.description=%s wring%s their hands constantly, especially when nervous\ + \ or deep in thought. + PersonalityQuirk.HANDSHAKE.text=Overly Enthusiastic Handshake -PersonalityQuirk.HANDSHAKE.description=%s give%s very firm and enthusiastic handshakes, often surprising or overwhelming others. +PersonalityQuirk.HANDSHAKE.description=%s give%s very firm and enthusiastic handshakes, often surprising\ + \ or overwhelming others. + PersonalityQuirk.HEADPHONES.text=Always Wearing Headphones -PersonalityQuirk.HEADPHONES.description=%s constantly wear%s headphones, sometimes even during conversations, listening to music or ambient sounds. +PersonalityQuirk.HEADPHONES.description=%s constantly wear%s headphones, sometimes even during\ + \ conversations, listening to music or ambient sounds. + PersonalityQuirk.HEALTHY_SNACKS.text=Frequently Snacking on Healthy Foods -PersonalityQuirk.HEALTHY_SNACKS.description=%s alway%s has a supply of healthy snacks, like carrot sticks or nuts, and encourages others to eat healthily too. +PersonalityQuirk.HEALTHY_SNACKS.description=%s alway%s has a supply of healthy snacks, like carrot\ + \ sticks or nuts, and encourages others to eat healthily too. + PersonalityQuirk.HISTORIAN.text=Passionate about History -PersonalityQuirk.HISTORIAN.description=%s frequently reference%s historical events and figures in conversation, relating them to current situations. +PersonalityQuirk.HISTORIAN.description=%s frequently reference%s historical events and figures in\ + \ conversation, relating them to current situations. + PersonalityQuirk.HUMMER.text=Habitual Hummer -PersonalityQuirk.HUMMER.description=%s often hum%s songs or tunes to themselves, especially when concentrating on a task. +PersonalityQuirk.HUMMER.description=%s often hum%s songs or tunes to themselves, especially when\ + \ concentrating on a task. + PersonalityQuirk.HYGIENIC.text=Obsessed with Hygiene -PersonalityQuirk.HYGIENIC.description=%s overly display%s concern with personal hygiene, constantly washing their hands and using sanitizer. +PersonalityQuirk.HYGIENIC.description=%s overly display%s concern with personal hygiene, constantly\ + \ washing their hands and using sanitizer. + PersonalityQuirk.IRREGULAR_SLEEPER.text=Unusual Sleep Patterns -PersonalityQuirk.IRREGULAR_SLEEPER.description=%s maintain%s a highly irregular sleep schedule, such as taking multiple short naps throughout the day or working best in the early morning hours. +PersonalityQuirk.IRREGULAR_SLEEPER.description=%s maintain%s a highly irregular sleep schedule, such\ + \ as taking multiple short naps throughout the day or working best in the early morning hours. + PersonalityQuirk.JOKER.text=Fond of Puns -PersonalityQuirk.JOKER.description=%s love%s making puns and wordplay, often inserting them into conversations, regardless of the appropriateness. +PersonalityQuirk.JOKER.description=%s love%s making puns and wordplay, often inserting them into\ + \ conversations, regardless of the appropriateness. + PersonalityQuirk.LISTS.text=Compulsive List Maker -PersonalityQuirk.LISTS.description=%s make%s lists for everything, from daily tasks to long-term goals, and checks them obsessively. +PersonalityQuirk.LISTS.description=%s make%s lists for everything, from daily tasks to long-term\ + \ goals, and checks them obsessively. + PersonalityQuirk.LITERAL.text=Overly Literal -PersonalityQuirk.LITERAL.description=%s take%s things very literally and often struggles to understand jokes, sarcasm, or figurative language. +PersonalityQuirk.LITERAL.description=%s take%s things very literally and often struggles to\ + \ understand jokes, sarcasm, or figurative language. + PersonalityQuirk.LOCKS.text=Checks Locks Repeatedly -PersonalityQuirk.LOCKS.description=%s habitually check%s doors and locks multiple times to ensure they're secure, even in familiar places. +PersonalityQuirk.LOCKS.description=%s habitually check%s doors and locks multiple times to ensure\ + \ they're secure, even in familiar places. + PersonalityQuirk.MEASURED_TALKER.text=Tends to Speak in a Measured Pace -PersonalityQuirk.MEASURED_TALKER.description=%s speak%s in a calm and measured pace, deliberately enunciating each word for clarity. +PersonalityQuirk.MEASURED_TALKER.description=%s speak%s in a calm and measured pace, deliberately\ + \ enunciating each word for clarity. + PersonalityQuirk.MINIMALIST.text=Extreme Minimalism -PersonalityQuirk.MINIMALIST.description=%s practice%s extreme minimalism, owning very few personal items and keeping their living space stark and uncluttered. +PersonalityQuirk.MINIMALIST.description=%s practice%s extreme minimalism, owning very few personal\ + \ items and keeping their living space stark and uncluttered. + PersonalityQuirk.MUG.text=Prefers Using a Specific Mug -PersonalityQuirk.MUG.description=%s use%s a specific mug for drinking coffee or tea and feels uncomfortable using other mugs. +PersonalityQuirk.MUG.description=%s use%s a specific mug for drinking coffee or tea and feels\ + \ uncomfortable using other mugs. + PersonalityQuirk.NAIL_BITER.text=Constant Nail Biter -PersonalityQuirk.NAIL_BITER.description=%s exhibit%s a habit of biting their nails, especially when they're anxious or deep in thought. +PersonalityQuirk.NAIL_BITER.description=%s exhibit%s a habit of biting their nails, especially when\ + \ they're anxious or deep in thought. + PersonalityQuirk.NICKNAMING.text=Frequent Nicknaming -PersonalityQuirk.NICKNAMING.description=%s display%s a tendency to give nicknames to everyone they meet, including their own equipment, and uses these nicknames exclusively. +PersonalityQuirk.NICKNAMING.description=%s display%s a tendency to give nicknames to everyone they\ + \ meet, including their own equipment, and uses these nicknames exclusively. + PersonalityQuirk.NIGHT_OWL.text=Night Owl -PersonalityQuirk.NIGHT_OWL.description=%s often work%s late into the night, often tweaking their equipment or reviewing mission data even when it's not necessary. +PersonalityQuirk.NIGHT_OWL.description=%s often work%s late into the night, often tweaking their\ + \ equipment or reviewing mission data even when it's not necessary. + PersonalityQuirk.NOTE_TAKER.text=Compulsive Note-Taking -PersonalityQuirk.NOTE_TAKER.description=%s keep%s detailed notes about everything, from mission briefings to casual conversations, filling numerous notebooks with meticulous records. +PersonalityQuirk.NOTE_TAKER.description=%s keep%s detailed notes about everything, from mission\ + \ briefings to casual conversations, filling numerous notebooks with meticulous records. + PersonalityQuirk.NOTEBOOK.text=Always Carrying a Notebook -PersonalityQuirk.NOTEBOOK.description=%s alway%s has a small notebook to jot down thoughts, ideas, or sketches at a moment's notice. +PersonalityQuirk.NOTEBOOK.description=%s alway%s has a small notebook to jot down thoughts, ideas,\ + \ or sketches at a moment's notice. + PersonalityQuirk.OBJECT.text=Carries a Personal Object -PersonalityQuirk.OBJECT.description=%s alway%s carries a small personal object, like a lucky coin or a memento, and keeps it close for comfort. +PersonalityQuirk.OBJECT.description=%s alway%s carries a small personal object, like a lucky coin or\ + \ a memento, and keeps it close for comfort. + PersonalityQuirk.ORGANIZATIONAL_TENDENCIES.text=Obsessive Organizational Tendencies -PersonalityQuirk.ORGANIZATIONAL_TENDENCIES.description=%s arrange%s their personal items or workspace with extreme precision and becomes agitated if things are out of place. +PersonalityQuirk.ORGANIZATIONAL_TENDENCIES.description=%s arrange%s their personal items or workspace\ + \ with extreme precision and becomes agitated if things are out of place. + PersonalityQuirk.ORGANIZER.text=Always Organizing -PersonalityQuirk.ORGANIZER.description=%s maintain%s a habit of reorganizing their desk or workspace, aligning items meticulously, even if they were already in order. +PersonalityQuirk.ORGANIZER.description=%s maintain%s a habit of reorganizing their desk or workspace,\ + \ aligning items meticulously, even if they were already in order. + PersonalityQuirk.ORIGAMI.text=Fond of Origami -PersonalityQuirk.ORIGAMI.description=%s love%s making origami figures and often leaves them around for others to find. +PersonalityQuirk.ORIGAMI.description=%s love%s making origami figures and often leaves them around\ + \ for others to find. + PersonalityQuirk.OVER_PLANNER.text=Obsessive Over-Planner -PersonalityQuirk.OVER_PLANNER.description=%s create%s detailed plans and schedules for everything, often over-preparing for even simple tasks. +PersonalityQuirk.OVER_PLANNER.description=%s create%s detailed plans and schedules for everything,\ + \ often over-preparing for even simple tasks. + PersonalityQuirk.OVEREXPLAINER.text=Chronic Overexplainer -PersonalityQuirk.OVEREXPLAINER.description=%s tend%s to over-explain even the simplest concepts, often providing excessive detail and background information. +PersonalityQuirk.OVEREXPLAINER.description=%s tend%s to over-explain even the simplest concepts,\ + \ often providing excessive detail and background information. + PersonalityQuirk.PEN_CLICKER.text=Habitual Pen Clicker -PersonalityQuirk.PEN_CLICKER.description=%s often click%s their pen repeatedly, especially when thinking or nervous, which can be distracting to others. +PersonalityQuirk.PEN_CLICKER.description=%s often click%s their pen repeatedly, especially when\ + \ thinking or nervous, which can be distracting to others. + PersonalityQuirk.PEN_TWIRLER.text=Habitual Pen Twirler -PersonalityQuirk.PEN_TWIRLER.description=%s often twirl%s a pen or pencil between their fingers, especially when deep in thought or waiting. +PersonalityQuirk.PEN_TWIRLER.description=%s often twirl%s a pen or pencil between their fingers,\ + \ especially when deep in thought or waiting. + PersonalityQuirk.PERSONIFICATION.text=Overly Friendly with Equipment -PersonalityQuirk.PERSONIFICATION.description=%s humanize%s their equipment, talking to it as if it were a living being, and gets distressed if others don't show the same respect. +PersonalityQuirk.PERSONIFICATION.description=%s humanize%s their equipment, talking to it as if it\ + \ were a living being, and gets distressed if others don't show the same respect. + PersonalityQuirk.PESSIMIST.text=Habitual Pessimist -PersonalityQuirk.PESSIMIST.description=%s alway%s expects the worst-case scenario and often verbalizes their negative expectations, dampening the mood of others. +PersonalityQuirk.PESSIMIST.description=%s alway%s expects the worst-case scenario and often verbalizes\ + \ their negative expectations, dampening the mood of others. + PersonalityQuirk.PHRASES.text=Tends to Use Specific Phrases -PersonalityQuirk.PHRASES.description=%s use%s a few catchphrases or specific expressions frequently in conversation, making their speech distinctive. +PersonalityQuirk.PHRASES.description=%s use%s a few catchphrases or specific expressions frequently\ + \ in conversation, making their speech distinctive. + PersonalityQuirk.PLANTS.text=Loves Plants -PersonalityQuirk.PLANTS.description=%s keep%s a variety of potted plants and talks to them as if they were pets, often giving them names. +PersonalityQuirk.PLANTS.description=%s keep%s a variety of potted plants and talks to them as if\ + \ they were pets, often giving them names. + PersonalityQuirk.POLITE.text=Excessive Politeness -PersonalityQuirk.POLITE.description=%s practice%s excessive politeness, always saying please and thank you, often apologizing for things that aren't their fault. +PersonalityQuirk.POLITE.description=%s practice%s excessive politeness, always saying please and\ + \ thank you, often apologizing for things that aren't their fault. + PersonalityQuirk.PRACTICAL_JOKER.text=Loves Practical Jokes -PersonalityQuirk.PRACTICAL_JOKER.description=%s enjoy%s playing practical jokes on their comrades, sometimes going to great lengths to set up elaborate pranks. +PersonalityQuirk.PRACTICAL_JOKER.description=%s enjoy%s playing practical jokes on their comrades,\ + \ sometimes going to great lengths to set up elaborate pranks. + PersonalityQuirk.PREPARED.text=Always Prepared -PersonalityQuirk.PREPARED.description=%s wear%s a wide variety of tools and supplies at all times, prepared for almost any situation. +PersonalityQuirk.PREPARED.description=%s wear%s a wide variety of tools and supplies at all times,\ + \ prepared for almost any situation. + PersonalityQuirk.PUNCTUAL.text=Overly Punctual -PersonalityQuirk.PUNCTUAL.description=%s alway%s arrives extremely early to meetings or events, sometimes to an inconvenient extent. +PersonalityQuirk.PUNCTUAL.description=%s alway%s arrives extremely early to meetings or events,\ + \ sometimes to an inconvenient extent. + PersonalityQuirk.PUZZLES.text=Obsessed with Puzzles -PersonalityQuirk.PUZZLES.description=%s alway%s has a puzzle on hand, whether it's a Rubik's cube, crossword, or sudoku, and solves them during downtime. +PersonalityQuirk.PUZZLES.description=%s alway%s has a puzzle on hand, whether it's a Rubik's cube,\ + \ crossword, or sudoku, and solves them during downtime. + PersonalityQuirk.QUOTES.text=Collects Quotes -PersonalityQuirk.QUOTES.description=%s show%s habit of collecting and reciting quotes from famous people, books, or movies, often at random moments. +PersonalityQuirk.QUOTES.description=%s show%s habit of collecting and reciting quotes from famous\ + \ people, books, or movies, often at random moments. + PersonalityQuirk.RARELY_SLEEPS.text=Rarely Sleeps -PersonalityQuirk.RARELY_SLEEPS.description=%s claim%s to need very little sleep and often stays awake for long periods, sometimes to the detriment of their health. +PersonalityQuirk.RARELY_SLEEPS.description=%s claim%s to need very little sleep and often stays awake\ + \ for long periods, sometimes to the detriment of their health. + PersonalityQuirk.ROUTINE.text=Has a Routine for Small Tasks -PersonalityQuirk.ROUTINE.description=%s follow%s a specific routine for small daily tasks, like making coffee or setting up their workspace, and feels uneasy if it's disrupted. +PersonalityQuirk.ROUTINE.description=%s follow%s a specific routine for small daily tasks, like making\ + \ coffee or setting up their workspace, and feels uneasy if it's disrupted. + PersonalityQuirk.SEEKS_APPROVAL.text=Constantly Seeking Approval -PersonalityQuirk.SEEKS_APPROVAL.description=%s demonstrate%s habit of constantly seeking validation or approval from peers or superiors, often to the point of being repetitive. +PersonalityQuirk.SEEKS_APPROVAL.description=%s demonstrate%s habit of constantly seeking validation\ + \ or approval from peers or superiors, often to the point of being repetitive. + PersonalityQuirk.SENTIMENTAL.text=Overly Sentimental -PersonalityQuirk.SENTIMENTAL.description=%s keep%s sentimental items like old letters, trinkets, and mementos, often reminiscing about their past. +PersonalityQuirk.SENTIMENTAL.description=%s keep%s sentimental items like old letters, trinkets, and\ + \ mementos, often reminiscing about their past. + PersonalityQuirk.SHARPENING.text=Compulsive Sharpening -PersonalityQuirk.SHARPENING.description=%s frequently sharpen%s pencils, knives, or other tools, often far more than necessary. +PersonalityQuirk.SHARPENING.description=%s frequently sharpen%s pencils, knives, or other tools,\ + \ often far more than necessary. + PersonalityQuirk.SINGS.text=Sings to Themselves -PersonalityQuirk.SINGS.description=%s often sing%s softly to themselves, especially when they think no one is listening. +PersonalityQuirk.SINGS.description=%s often sing%s softly to themselves, especially when they think\ + \ no one is listening. + PersonalityQuirk.SKEPTICAL.text=Chronically Skeptical -PersonalityQuirk.SKEPTICAL.description=%s alway%s questions and doubts new information or ideas, needing significant proof before believing anything. +PersonalityQuirk.SKEPTICAL.description=%s alway%s questions and doubts new information or ideas,\ + \ needing significant proof before believing anything. + PersonalityQuirk.SLEEP_TALKER.text=Talks in Sleep -PersonalityQuirk.SLEEP_TALKER.description=%s often talk%s in their sleep, sometimes revealing random or humorous thoughts. +PersonalityQuirk.SLEEP_TALKER.description=%s often talk%s in their sleep, sometimes revealing random\ + \ or humorous thoughts. + PersonalityQuirk.SMILER.text=Compulsive Smiler -PersonalityQuirk.SMILER.description=%s smile%s constantly, even in situations where it might not be appropriate, often confusing others. +PersonalityQuirk.SMILER.description=%s smile%s constantly, even in situations where it might not be\ + \ appropriate, often confusing others. + PersonalityQuirk.SNACKS.text=Always Has a Snack -PersonalityQuirk.SNACKS.description=%s keep%s snacks with them at all times and is often seen munching on something. +PersonalityQuirk.SNACKS.description=%s keep%s snacks with them at all times and is often seen munching\ + \ on something. + PersonalityQuirk.STORYTELLING.text=Compulsive Storytelling -PersonalityQuirk.STORYTELLING.description=%s show%s an uncontrollable urge to share detailed stories or anecdotes, often inappropriately or at length, even when others aren't interested. +PersonalityQuirk.STORYTELLING.description=%s show%s an uncontrollable urge to share detailed stories\ + \ or anecdotes, often inappropriately or at length, even when others aren't interested. + PersonalityQuirk.STRETCHING.text=Constantly Stretching -PersonalityQuirk.STRETCHING.description=%s exhibit%s a habit of stretching and doing light exercises at random times, even during meetings. +PersonalityQuirk.STRETCHING.description=%s exhibit%s a habit of stretching and doing light exercises\ + \ at random times, even during meetings. + PersonalityQuirk.SUPERSTITIOUS_RITUALS.text=Superstitious Rituals -PersonalityQuirk.SUPERSTITIOUS_RITUALS.description=%s perform%s specific rituals or carries lucky charms before each mission, believing they influence the outcome. +PersonalityQuirk.SUPERSTITIOUS_RITUALS.description=%s perform%s specific rituals or carries lucky\ + \ charms before each mission, believing they influence the outcome. + PersonalityQuirk.SUPERVISED_HABITS.text=Highly Supervised Habits -PersonalityQuirk.SUPERVISED_HABITS.description=%s insist%s on having their personal gear checked or cleaned by a specific person or in a specific way, creating a mini-routine that must be followed. +PersonalityQuirk.SUPERVISED_HABITS.description=%s insist%s on having their personal gear checked or\ + \ cleaned by a specific person or in a specific way, creating a mini-routine that must be followed. + PersonalityQuirk.TECH_TALK.text=Incessant Tech Talk -PersonalityQuirk.TECH_TALK.description=%s demonstrate%s a habit of explaining the inner workings of their equipment to anyone who will listen to them, even if it's not relevant to the conversation. +PersonalityQuirk.TECH_TALK.description=%s demonstrate%s a habit of explaining the inner workings of\ + \ their equipment to anyone who will listen to them, even if it's not relevant to the conversation. + PersonalityQuirk.TECHNOPHOBIA.text=Phobia of Technology -PersonalityQuirk.TECHNOPHOBIA.description=%s show%s a peculiar fear or aversion to certain types of technology, like computers or certain systems. +PersonalityQuirk.TECHNOPHOBIA.description=%s show%s a peculiar fear or aversion to certain types of\ + \ technology, like computers or certain systems. + PersonalityQuirk.THESAURUS.text=Uses Obscure Words -PersonalityQuirk.THESAURUS.description=%s enjoy%s using rare and obscure words in conversation, often confusing others but expanding their vocabulary. +PersonalityQuirk.THESAURUS.description=%s enjoy%s using rare and obscure words in conversation, often\ + \ confusing others but expanding their vocabulary. + PersonalityQuirk.THIRD_PERSON.text=Speaks in Third Person -PersonalityQuirk.THIRD_PERSON.description=%s occasionally refer%s to themselves in the third person, which can confuse or amuse others. +PersonalityQuirk.THIRD_PERSON.description=%s occasionally refer%s to themselves in the third person,\ + \ which can confuse or amuse others. + PersonalityQuirk.TIME_MANAGEMENT.text=Obsessed with Time Management -PersonalityQuirk.TIME_MANAGEMENT.description=%s optimize%s their time, often using timers and schedules down to the minute. +PersonalityQuirk.TIME_MANAGEMENT.description=%s optimize%s their time, often using timers and schedules\ + \ down to the minute. + PersonalityQuirk.TINKERER.text=Compulsive Tinkerer -PersonalityQuirk.TINKERER.description=%s constantly play%s with gadgets, tools, or their equipment, even if nothing is broken or needs adjustment. +PersonalityQuirk.TINKERER.description=%s constantly play%s with gadgets, tools, or their equipment,\ + \ even if nothing is broken or needs adjustment. + PersonalityQuirk.TRUTH_TELLER.text=Habitual Truth-Teller -PersonalityQuirk.TRUTH_TELLER.description=%s hold%s a compulsion to always tell the truth, even when a white lie might be more appropriate or polite. +PersonalityQuirk.TRUTH_TELLER.description=%s hold%s a compulsion to always tell the truth, even when\ + \ a white lie might be more appropriate or polite. + PersonalityQuirk.UNNECESSARY_CAUTION.text=Unnecessary Caution -PersonalityQuirk.UNNECESSARY_CAUTION.description=%s take%s excessive precautions in situations where it's not needed, like double-checking door locks multiple times or wearing extra safety gear for routine tasks. +PersonalityQuirk.UNNECESSARY_CAUTION.description=%s take%s excessive precautions in situations where\ + \ it's not needed, like double-checking door locks multiple times or wearing extra safety gear for\ + \ routine tasks. + PersonalityQuirk.UNPREDICTABLE_SPEECH.text=Unpredictable Speech -PersonalityQuirk.UNPREDICTABLE_SPEECH.description=%s use%s a manner of speaking that is highly unpredictable, such as frequently switching accents or using unusual metaphors and analogies. +PersonalityQuirk.UNPREDICTABLE_SPEECH.description=%s use%s a manner of speaking that is highly\ + \ unpredictable, such as frequently switching accents or using unusual metaphors and analogies. + PersonalityQuirk.UNUSUAL_HOBBIES.text=Unusual Hobbies -PersonalityQuirk.UNUSUAL_HOBBIES.description=%s practice%s an unusual or unexpected hobby, like competitive stamp collecting or building intricate models, which he/she is passionate about. +PersonalityQuirk.UNUSUAL_HOBBIES.description=%s practice%s an unusual or unexpected hobby, like\ + \ competitive stamp collecting or building intricate models, which he/she is passionate about. + PersonalityQuirk.WATCH.text=Constantly Checking the Time -PersonalityQuirk.WATCH.description=%s frequently check%s their watch or phone for the time, even if they aren't on a tight schedule. +PersonalityQuirk.WATCH.description=%s frequently check%s their watch or phone for the time, even if\ + \ they aren't on a tight schedule. + PersonalityQuirk.WEATHERMAN.text=Obsessed with Weather -PersonalityQuirk.WEATHERMAN.description=%s is alway%s checking the weather forecast and talks about it frequently, even when it's not relevant. +PersonalityQuirk.WEATHERMAN.description=%s is alway%s checking the weather forecast and talks about\ + \ it frequently, even when it's not relevant. + PersonalityQuirk.WHISTLER.text=Frequent Whistler -PersonalityQuirk.WHISTLER.description=%s frequently whistle%s tunes under their breath, often at inappropriate times. +PersonalityQuirk.WHISTLER.description=%s frequently whistle%s tunes under their breath, often at\ + \ inappropriate times. + PersonalityQuirk.WORRIER.text=Persistent Worrier -PersonalityQuirk.WORRIER.description=%s demonstrate%s a habit of constantly worrying excessively about small details and potential problems, often voicing their concerns aloud. +PersonalityQuirk.WORRIER.description=%s demonstrate%s a habit of constantly worrying excessively about\ + \ small details and potential problems, often voicing their concerns aloud. + PersonalityQuirk.WRITER.text=Writes Everything Down -PersonalityQuirk.WRITER.description=%s keep%s a detailed journal of daily events and thoughts, writing everything down meticulously. +PersonalityQuirk.WRITER.description=%s keep%s a detailed journal of daily events and thoughts, writing\ + \ everything down meticulously. + +PersonalityQuirk.BATTLEFIELD_NOSTALGIA.text=Constantly Reminiscing +PersonalityQuirk.BATTLEFIELD_NOSTALGIA.description=%s often recount%s old battles in vivid detail,\ + \ sometimes becoming distracted by memories during downtime or even in the heat of combat. + +PersonalityQuirk.HEAVY_HANDED.text=Heavy-Handed With Equipment +PersonalityQuirk.HEAVY_HANDED.description=%s tend%s to handle equipment and tools with excessive force,\ + \ often resulting in unnecessary damage or wear, much to the chagrin of the tech crew. + +PersonalityQuirk.RATION_HOARDER.text=Hoarding Rations +PersonalityQuirk.RATION_HOARDER.description=%s stash%s extra rations in their personal quarters,\ + \ believing that an unexpected shortage could happen at any moment. + +PersonalityQuirk.EMERGENCY_MANUAL_READER.text=Obsessive Manual Reader +PersonalityQuirk.EMERGENCY_MANUAL_READER.description=%s read%s and re-reads emergency procedure manuals,\ + \ sometimes even quoting them verbatim during combat, which can be both helpful and annoying. + +PersonalityQuirk.QUICK_TO_QUIP.text=Quick to Make Jokes Under Fire +PersonalityQuirk.QUICK_TO_QUIP.description=%s often crack%s jokes or make sarcastic comments during \ + intense combat situations, using humor as a coping mechanism, even if it irritates their lancemates. + +PersonalityQuirk.TECH_SKEPTIC.text=Distrusts New Technology +PersonalityQuirk.TECH_SKEPTIC.description=%s are wary of new tech and equipment, preferring older, \ + proven models over the latest innovations, and often express%s concern over potential malfunctions. + +PersonalityQuirk.POST_BATTLE_RITUALS.text=Performs Post-Battle Rituals +PersonalityQuirk.POST_BATTLE_RITUALS.description=%s perform%s specific personal rituals after each \ + mission, such as polishing armor in a precise order or reciting a mantra, believing it keeps luck\ + \ on their side. + +PersonalityQuirk.OVER_COMMUNICATOR.text=Over-Explains in Combat +PersonalityQuirk.OVER_COMMUNICATOR.description=%s over-explain%s their tactical moves or give detailed\ + \ status reports over comms, causing unnecessary chatter that can sometimes clutter communication\ + \ channels. + +PersonalityQuirk.FIELD_MEDIC.text=Tends to Perform Medical Aid +PersonalityQuirk.FIELD_MEDIC.description=%s feel%s compelled to provide medical aid whenever possible,\ + \ even if it's not their designated role, often carrying extra medical supplies for emergencies. + +PersonalityQuirk.SYSTEM_CALIBRATOR.text=Constantly Calibrates Systems +PersonalityQuirk.SYSTEM_CALIBRATOR.description=%s continually calibrate%s their equipment, believing\ + \ that even a fraction of a percent improvement could be crucial in battle, often at the expense of\ + \ downtime relaxation. + +PersonalityQuirk.AMMO_COUNTER.text=Obsessive Ammo Counter +PersonalityQuirk.AMMO_COUNTER.description=%s obsessively count%s remaining ammo during battles,\ + \ constantly recalculating how much they have left and adjusting tactics accordingly. + +PersonalityQuirk.BRAVADO.text=Excessive Bravado +PersonalityQuirk.BRAVADO.description=%s exhibit%s excessive bravado, taunting enemies over comms or\ + \ taking unnecessary risks to demonstrate courage, often at the expense of safety. + +PersonalityQuirk.COMBAT_SONG.text=Sings in Combat +PersonalityQuirk.COMBAT_SONG.description=%s sing%s loud battle songs or hums tunes during combat,\ + \ claiming it boosts morale, though it can be distracting to others. + +PersonalityQuirk.COMMS_TOGGLE.text=Constantly Toggles Comms Channels +PersonalityQuirk.COMMS_TOGGLE.description=%s frequently toggle%s between comms channels, often causing\ + \ confusion or miscommunication during critical moments. + +PersonalityQuirk.EJECTION_READY.text=Paranoid About Ejection Systems +PersonalityQuirk.EJECTION_READY.description=%s constantly check%s their Mek's ejection system, ensuring\ + \ it's ready in case of catastrophic failure, sometimes to a point of paranoia. + +PersonalityQuirk.HAND_SIGNS.text=Uses Elaborate Hand Signals +PersonalityQuirk.HAND_SIGNS.description=%s use%s complex hand signals to communicate with nearby\ + \ squadmates, even when voice comms would be more efficient. + +PersonalityQuirk.HATE_FOR_MEKS.text=Harbors a Deep Hatred for Meks +PersonalityQuirk.HATE_FOR_MEKS.description=%s harbor%s a deep disdain for Meks, considering them\ + \ necessary evils, often vocalizing their dislike even if piloting one. + +PersonalityQuirk.IMPROVISED_WEAPONRY.text=Uses Improvised Weaponry +PersonalityQuirk.IMPROVISED_WEAPONRY.description=%s frequently improvise%s weaponry during combat,\ + \ adapting objects around them into makeshift weapons when necessary. + +PersonalityQuirk.PRE_BATTLE_SUPERSTITIONS.text=Pre-Battle Superstitions +PersonalityQuirk.PRE_BATTLE_SUPERSTITIONS.description=%s follow%s a strict series of superstitious\ + \ rituals before every mission, believing that any deviation could lead to failure. + +PersonalityQuirk.SILENT_LEADER.text=Silent Leader +PersonalityQuirk.SILENT_LEADER.description=%s lead%s by example rather than words, rarely speaking but\ + \ making decisive, impactful moves that inspire others to follow suit. + +PersonalityQuirk.BATTLE_CRITIC.text=Constant Battle Critic +PersonalityQuirk.BATTLE_CRITIC.description=%s constantly critique%s the combat performance of others,\ + \ offering unsolicited advice on tactics, positioning, or equipment use. + +PersonalityQuirk.CHECKS_WEAPON_SAFETY.text=Constantly Checks Weapon Safety +PersonalityQuirk.CHECKS_WEAPON_SAFETY.description=%s frequently check%s the safety settings of their\ + \ weapons, even in the middle of combat, to ensure no accidental misfire occurs. + +PersonalityQuirk.CLOSE_COMBAT_PREF.text=Prefers Close Combat +PersonalityQuirk.CLOSE_COMBAT_PREF.description=%s alway%s seek to close the distance with enemies,\ + \ favoring melee attacks over ranged options whenever possible, even when it's risky. + +PersonalityQuirk.COMBAT_POET.text=Recites Poetry in Combat +PersonalityQuirk.COMBAT_POET.description=%s recite%s lines of poetry or philosophical quotes during\ + \ intense firefights, adding a dramatic flair to their combat style. + +PersonalityQuirk.CUSTOM_DECALS.text=Obsessed with Custom Decals +PersonalityQuirk.CUSTOM_DECALS.description=%s personalize%s their equipment with unique decals and\ + \ paint schemes, treating the exterior as a canvas for artistic expression. + +PersonalityQuirk.DISPLAYS_TROPHIES.text=Displays Trophies from Battles +PersonalityQuirk.DISPLAYS_TROPHIES.description=%s keep%s trophies from fallen enemies or successful\ + \ missions, often displaying them prominently in their quarters or on their equipment. + +PersonalityQuirk.DO_IT_YOURSELF.text=Obsessive Do-It-Yourselfer +PersonalityQuirk.DO_IT_YOURSELF.description=%s insist%s on performing all repairs and maintenance on\ + \ their equipment personally, trusting no one else with their gear. + +PersonalityQuirk.FIELD_IMPROVISER.text=Improvises with Field Supplies +PersonalityQuirk.FIELD_IMPROVISER.description=%s hold%s a knack for using field supplies in unconventional\ + \ ways, often creating makeshift tools or modifications during missions. + +PersonalityQuirk.LOUD_COMMS.text=Uses Loud Comms +PersonalityQuirk.LOUD_COMMS.description=%s speak%s loudly over comms, regardless of the situation,\ + \ often startling teammates or drowning out other communication. + +PersonalityQuirk.WAR_STORIES.text=Overly Fond of War Stories +PersonalityQuirk.WAR_STORIES.description=%s frequently recount%s old war stories, often embellishing \ + details for dramatic effect, sometimes repeating the same stories to the point of annoyance. + +PersonalityQuirk.ALL_OR_NOTHING.text=All or Nothing +PersonalityQuirk.ALL_OR_NOTHING.description=%s tend%s to go all out in battles, committing every\ + \ resource and pushing limits, often ignoring fallback plans or safety margins. + +PersonalityQuirk.BOOTS_ON_THE_GROUND.text=Prefers Ground Tactics +PersonalityQuirk.BOOTS_ON_THE_GROUND.description=%s feel%s more comfortable with ground-based tactics,\ + \ often favoring urban combat or rough terrain over open-field engagements. + +PersonalityQuirk.BRAVERY_BOASTER.text=Boasts About Bravery +PersonalityQuirk.BRAVERY_BOASTER.description=%s constantly boast%s about their acts of bravery,\ + \ sometimes embellishing stories to inspire comrades or boost their own morale. + +PersonalityQuirk.COCKPIT_DRIFTER.text=Daydreams +PersonalityQuirk.COCKPIT_DRIFTER.description=%s occasionally drift%s into daydreams while in combat,\ + \ sometimes losing focus at critical moments. + +PersonalityQuirk.CONSPIRACY_THEORIST.text=Believes in Conspiracy Theories +PersonalityQuirk.CONSPIRACY_THEORIST.description=%s often express%s a belief in various conspiracy\ + \ theories, speculating about hidden agendas behind every mission. + +PersonalityQuirk.DEVOUT_WARRIOR.text=Warrior's Code Devotee +PersonalityQuirk.DEVOUT_WARRIOR.description=%s strictly adhere%s to a personal code of honor in battle,\ + \ refusing to attack retreating foes or use certain weapons they consider dishonorable. + +PersonalityQuirk.DUAL_WIELDING.text=Obsessed with Dual Weapons +PersonalityQuirk.DUAL_WIELDING.description=%s hold%s a fascination with dual-wielding weapons,\ + \ favoring equipment that allows for simultaneous use of two similar weapons. + +PersonalityQuirk.EMBLEM_LOVER.text=Attached to Specific Emblem +PersonalityQuirk.EMBLEM_LOVER.description=%s are extremely attached to a personal emblem or insignia,\ + \ displaying it prominently on their equipment and personal belongings. + +PersonalityQuirk.EXCESSIVE_DEBRIEFING.text=Obsessive Debriefer +PersonalityQuirk.EXCESSIVE_DEBRIEFING.description=%s insist%s on detailed debriefings after every\ + \ mission, analyzing every decision and seeking feedback from every team member. + +PersonalityQuirk.EYE_FOR_ART.text=Appreciates Battlefield Scenery +PersonalityQuirk.EYE_FOR_ART.description=%s appreciate%s the scenery during battles, often commenting\ + \ on landscapes, ruins, or weather, even in the middle of combat. + +PersonalityQuirk.FAST_TALKER.text=Talks Very Fast +PersonalityQuirk.FAST_TALKER.description=%s speak%s very quickly, sometimes making it hard for others\ + \ to follow orders or understand battle reports. + +PersonalityQuirk.FINGER_GUNS.text=Uses Finger Guns +PersonalityQuirk.FINGER_GUNS.description=%s frequently use%s finger guns to emphasize points or joke\ + \ around, even in serious situations. + +PersonalityQuirk.FLARE_DEPLOYER.text=Excessive Flare Use +PersonalityQuirk.FLARE_DEPLOYER.description=%s use%s flares or smoke liberally, often deploying them\ + \ for dramatic effect or as a distraction tactic. + +PersonalityQuirk.FRIENDLY_INTERROGATOR.text=Likes to "Interrogate" Teammates +PersonalityQuirk.FRIENDLY_INTERROGATOR.description=%s enjoy%s asking teammates probing questions,\ + \ often as a game or way to uncover personal secrets. + +PersonalityQuirk.GUN_NUT.text=Fascinated by Firearms +PersonalityQuirk.GUN_NUT.description=%s hold%s a deep fascination with firearms, often modifying or\ + \ customizing their weapons to achieve "optimal performance." + +PersonalityQuirk.LAST_MAN_STANDING.text=Last Man Standing Mentality +PersonalityQuirk.LAST_MAN_STANDING.description=%s believe%s in being the last one to retreat, holding\ + \ the line until everyone else has withdrawn, even at great personal risk. + +PersonalityQuirk.LEGENDARY_MEK.text=Hunts The Black Marauder +PersonalityQuirk.LEGENDARY_MEK.description=%s hold%s a deep belief in the existence of the legendary,\ + \ nearly mythical Black Marauder and will pursue any rumors about it with enthusiasm. + +PersonalityQuirk.PASSIVE_LEADER.text=Leads Through Passivity +PersonalityQuirk.PASSIVE_LEADER.description=%s tend%s to lead through passive actions, guiding others\ + \ by subtle suggestions or letting them learn from mistakes. + +PersonalityQuirk.REBEL_WITHOUT_CAUSE.text=Rebellious Without Cause +PersonalityQuirk.REBEL_WITHOUT_CAUSE.description=%s exhibit%s a rebellious streak, often resisting\ + \ orders or established protocols for no clear reason. + +PersonalityQuirk.SIMPLE_LIFE.text=Prefers Simple Solutions +PersonalityQuirk.SIMPLE_LIFE.description=%s alway%s prefer straightforward solutions, often opting\ + \ for the most direct approach even when complex tactics might be better. + +PersonalityQuirk.ANTI_AUTHORITY.text=Distrusts Authority +PersonalityQuirk.ANTI_AUTHORITY.description=%s hold%s a strong distrust of authority figures, often\ + \ questioning orders or finding loopholes to avoid following them. + +PersonalityQuirk.BLOODLUST.text=Thrives in Combat Chaos +PersonalityQuirk.BLOODLUST.description=%s seem%s to revel in the chaos of combat, often becoming more\ + \ aggressive and reckless as battles intensify. + +PersonalityQuirk.BRAVERY_IN_DOUBT.text=Acts Bravely When Doubted +PersonalityQuirk.BRAVERY_IN_DOUBT.description=%s display%s sudden bursts of bravery when their courage\ + \ is called into question, often trying to prove doubters wrong. + +PersonalityQuirk.CLOSE_QUARTERS_ONLY.text=Prefers Close Quarters Combat +PersonalityQuirk.CLOSE_QUARTERS_ONLY.description=%s are uncomfortable with ranged combat and seek%s\ + \ opportunities to engage enemies in close quarters whenever possible. + +PersonalityQuirk.COOL_UNDER_FIRE.text=Remains Calm Under Fire +PersonalityQuirk.COOL_UNDER_FIRE.description=%s are unusually calm under fire, sometimes to the point\ + \ of appearing detached or overly casual about life-threatening situations. + +PersonalityQuirk.CRASH_TEST.text=Purposely Tests Equipment Durability +PersonalityQuirk.CRASH_TEST.description=%s purposefully put%s their equipment in situations to test\ + \ its durability, sometimes risking damage to satisfy their curiosity. + +PersonalityQuirk.DEAD_PAN_HUMOR.text=Uses Deadpan Humor +PersonalityQuirk.DEAD_PAN_HUMOR.description=%s frequently employ%s deadpan humor, often delivering\ + \ dry jokes that confuse others about whether they're serious or joking. + +PersonalityQuirk.DRILLS.text=Obsessed with Drills and Simulations +PersonalityQuirk.DRILLS.description=%s are obsessed with running drills and simulations, often pushing\ + \ the team to rehearse every scenario repeatedly. + +PersonalityQuirk.ENEMY_RESPECT.text=Respects Worthy Enemies +PersonalityQuirk.ENEMY_RESPECT.description=%s develop%s a respect for skilled opponents, sometimes\ + \ attempting to communicate or establish rapport with them, even during battle. + +PersonalityQuirk.EXTREME_MORNING_PERSON.text=Extremely Morning-Oriented +PersonalityQuirk.EXTREME_MORNING_PERSON.description=%s operate%s best in the early hours, often waking\ + \ up before dawn to run maintenance checks or train before the rest of the crew awakens. + +PersonalityQuirk.GALLANT.text=Gallant in Battle +PersonalityQuirk.GALLANT.description=%s display%s a gallant attitude in combat, often putting themselves\ + \ in harm's way to protect allies or civilians, sometimes unnecessarily. + +PersonalityQuirk.IRON_STOMACH.text=Can Eat Anything +PersonalityQuirk.IRON_STOMACH.description=%s hold%s an iron stomach, able to consume questionable\ + \ rations or battlefield-prepared meals without hesitation or complaint. + +PersonalityQuirk.MISSION_CRITIC.text=Hyper-Critical of Mission Plans +PersonalityQuirk.MISSION_CRITIC.description=%s tend%s to be overly critical of mission plans, always\ + \ suggesting improvements or expressing doubts about the current strategy. + +PersonalityQuirk.NO_PAIN_NO_GAIN.text=Believes in "No Pain, No Gain" +PersonalityQuirk.NO_PAIN_NO_GAIN.description=%s believe%s that hardship leads to success, often pushing\ + \ themselves and others to endure discomfort or pain to achieve goals. + +PersonalityQuirk.PERSONAL_ARMORY.text=Keeps a Personal Armory +PersonalityQuirk.PERSONAL_ARMORY.description=%s maintain%s a personal armory, collecting a variety\ + \ of weapons and gear, which they're constantly tinkering with or upgrading. + +PersonalityQuirk.QUICK_ADAPTER.text=Adapts Quickly to New Situations +PersonalityQuirk.QUICK_ADAPTER.description=%s adapt%s quickly to unexpected developments, often coming\ + \ up with impromptu solutions that surprise even their allies. + +PersonalityQuirk.RETALIATOR.text=Seeks Revenge for Any Slight +PersonalityQuirk.RETALIATOR.description=%s are extremely vengeful, seeking immediate retaliation for\ + \ any perceived slight or insult, whether in training or real combat. + +PersonalityQuirk.RUSH_HOUR.text=Enjoys the Heat of Battle +PersonalityQuirk.RUSH_HOUR.description=%s enjoy%s the adrenaline rush of intense combat, often looking\ + \ for opportunities to be at the center of action, even if it's risky. + +PersonalityQuirk.SILENT_PROTECTOR.text=Protects Allies Silently +PersonalityQuirk.SILENT_PROTECTOR.description=%s silently protect%s their teammates, often taking on\ + \ threats or positioning themselves defensively without drawing attention to their actions. + +PersonalityQuirk.ALWAYS_TACTICAL.text=Always Thinking Tactically +PersonalityQuirk.ALWAYS_TACTICAL.description=%s are constantly thinking in tactical terms, even in\ + \ everyday situations, often assessing people and places as potential combat scenarios. + +PersonalityQuirk.BATTLE_SCREAM.text=Uses a Battle Scream +PersonalityQuirk.BATTLE_SCREAM.description=%s unleash%s a loud battle cry when engaging enemies,\ + \ claiming it boosts morale and intimidates opponents. + +PersonalityQuirk.BRIEF_AND_TO_THE_POINT.text=Brief and To the Point +PersonalityQuirk.BRIEF_AND_TO_THE_POINT.description=%s speak%s in short, direct sentences, rarely\ + \ elaborating and often leaving others to interpret their meaning. + +PersonalityQuirk.CALLSIGN_COLLECTOR.text=Collects Callsigns +PersonalityQuirk.CALLSIGN_COLLECTOR.description=%s are fascinated by callsigns, often collecting\ + \ stories about how others earned theirs and giving nicknames to everyone they meet. + +PersonalityQuirk.CHATTERBOX.text=Constant Chatterbox +PersonalityQuirk.CHATTERBOX.description=%s talk%s constantly, even during intense combat situations,\ + \ often using chatter to relieve tension or distract foes. + +PersonalityQuirk.COMBAT_ARTIST.text=Combat as Art Form +PersonalityQuirk.COMBAT_ARTIST.description=%s view%s combat as an art form, seeking creative or\ + \ stylish ways to engage enemies and often incorporating flamboyant maneuvers. + +PersonalityQuirk.DARING_ESCAPE.text=Specializes in Daring Escapes +PersonalityQuirk.DARING_ESCAPE.description=%s are known for pulling off daring escapes, often getting\ + \ themselves into risky situations just to make the getaway more thrilling. + +PersonalityQuirk.DOOMSDAY_PREPPER.text=Battlefield Doomsday Prepper +PersonalityQuirk.DOOMSDAY_PREPPER.description=%s prepare%s excessively for worst-case scenarios, often\ + \ carrying extra survival gear and emergency supplies into combat. + +PersonalityQuirk.EQUIPMENT_SCAVENGER.text=Scavenges for Gear +PersonalityQuirk.EQUIPMENT_SCAVENGER.description=%s hold%s a habit of scavenging for useful gear on\ + \ the battlefield, often collecting parts and gadgets from fallen Meks or outposts. + +PersonalityQuirk.FRIEND_TO_FOES.text=Sympathetic to Enemies +PersonalityQuirk.FRIEND_TO_FOES.description=%s sometimes express%s sympathy for enemies, seeing them\ + \ as misguided or driven by unfortunate circumstances, even during battle. + +PersonalityQuirk.GUNG_HO.text=Gung-Ho Attitude +PersonalityQuirk.GUNG_HO.description=%s are extremely enthusiastic about combat missions, often\ + \ volunteering for the most dangerous tasks with boundless energy. + +PersonalityQuirk.INSPIRATIONAL_POET.text=Recites Inspirational Poems +PersonalityQuirk.INSPIRATIONAL_POET.description=%s recite%s inspirational poems or quotes during \ + intense moments, believing that words of wisdom can turn the tide of battle. + +PersonalityQuirk.MEK_MATCHMAKER.text=Believes in Mek Compatibility +PersonalityQuirk.MEK_MATCHMAKER.description=%s believe%s that certain Meks and pilots are "meant to\ + \ be," often assigning personalities or traits to Meks based on their pilots. + +PersonalityQuirk.MISSILE_JUNKIE.text=Missile Enthusiast +PersonalityQuirk.MISSILE_JUNKIE.description=%s hold%s a fascination with missiles, preferring loadouts\ + \ that maximize explosive firepower and often pushing for missile-heavy strategies. + +PersonalityQuirk.NEVER_RETREAT.text=Never Retreats +PersonalityQuirk.NEVER_RETREAT.description=%s refuse%s to retreat under any circumstances, even when\ + \ tactically advisable, seeing it as a personal code of honor. + +PersonalityQuirk.OPTIMISTIC_TO_A_FAULT.text=Optimistic to a Fault +PersonalityQuirk.OPTIMISTIC_TO_A_FAULT.description=%s maintain%s an overly optimistic outlook, always\ + \ expecting the best outcome even in dire situations, sometimes underestimating risks. + +PersonalityQuirk.REACTIVE.text=Highly Reactive +PersonalityQuirk.REACTIVE.description=%s respond%s instantly to any threat or provocation, often\ + \ acting impulsively without waiting for orders or analyzing the situation. + +PersonalityQuirk.RISK_TAKER.text=Extreme Risk Taker +PersonalityQuirk.RISK_TAKER.description=%s are drawn to extreme risks, often attempting dangerous\ + \ maneuvers or bold strategies regardless of potential consequences. + +PersonalityQuirk.SIGNATURE_MOVE.text=Has a Signature Combat Move +PersonalityQuirk.SIGNATURE_MOVE.description=%s hold%s developed a signature combat move that they use\ + \ whenever possible, often as a personal trademark in battle. + +PersonalityQuirk.TACTICAL_WITHDRAWAL.text=Strategically Withdraws +PersonalityQuirk.TACTICAL_WITHDRAWAL.description=%s are experts at tactical retreats, knowing exactly\ + \ when to pull back to avoid unnecessary losses while regrouping for a counterattack. + +PersonalityQuirk.ACCENT_SWITCHER.text=Switches Accents Randomly +PersonalityQuirk.ACCENT_SWITCHER.description=%s frequently switch%s accents while speaking, often\ + \ confusing allies or attempting to imitate enemy factions. + +PersonalityQuirk.AMBUSH_LOVER.text=Ambush Specialist +PersonalityQuirk.AMBUSH_LOVER.description=%s excel%s at setting up ambushes, always looking for ways\ + \ to lure enemies into traps and strike from unexpected angles. + +PersonalityQuirk.BATTLE_HARDENED.text=Battle-Hardened Stoic +PersonalityQuirk.BATTLE_HARDENED.description=%s display%s little emotion in combat, maintaining a\ + \ stoic demeanor regardless of the situation, often unnerving their allies and enemies alike. + +PersonalityQuirk.BREAKS_RADIO_SILENCE.text=Breaks Radio Silence +PersonalityQuirk.BREAKS_RADIO_SILENCE.description=%s often break%s radio silence during missions,\ + \ either to share updates, make jokes, or offer reassurance, even when stealth is crucial. + +PersonalityQuirk.CONVOY_LOVER.text=Prefers Convoy Missions +PersonalityQuirk.CONVOY_LOVER.description=%s hold%s a strong preference for convoy escort missions,\ + \ enjoying the sense of responsibility and defensive coordination. + +PersonalityQuirk.DEBRIS_SLINGER.text=Uses Debris as Weapons +PersonalityQuirk.DEBRIS_SLINGER.description=%s frequently use%s battlefield debris as makeshift\ + \ weapons or cover, adapting quickly to whatever the environment provides. + +PersonalityQuirk.CAMOUFLAGE.text=Likes to Camouflage Equipment +PersonalityQuirk.CAMOUFLAGE.description=%s love%s camouflaging their equipment to blend into the\ + \ environment, often spending extra time on detailed paint jobs and coverings. + +PersonalityQuirk.DISTANT_LEADER.text=Leads from a Distance +PersonalityQuirk.DISTANT_LEADER.description=%s prefer%s to lead from the rear, giving orders and\ + \ managing tactics remotely while avoiding direct confrontation. + +PersonalityQuirk.DRAMATIC_FINISH.text=Prefers Dramatic Finishes +PersonalityQuirk.DRAMATIC_FINISH.description=%s alway%s aim for a dramatic conclusion in battles,\ + \ whether it's a final charge, an over-the-top shot, or a flashy victory pose. + +PersonalityQuirk.ENGINE_REVERER.text=Reveres Fusion Engines +PersonalityQuirk.ENGINE_REVERER.description=%s hold%s a deep reverence for fusion engines, treating \ + them like the heart of the machine and performing rituals before every mission. + +PersonalityQuirk.FLIRTY_COMMS.text=Flirts Over Comms +PersonalityQuirk.FLIRTY_COMMS.description=%s frequently flirt%s over the comms, using charm as a\ + \ distraction or morale boost, often to the amusement or annoyance of their teammates. + +PersonalityQuirk.FOCUS_FREAK.text=Hyper-Focused Under Pressure +PersonalityQuirk.FOCUS_FREAK.description=%s become%s hyper-focused in critical moments, often tuning \ + out everything except the immediate threat, which can be both an asset and a weakness. + +PersonalityQuirk.FOUL_MOUTHED.text=Foul-Mouthed in Combat +PersonalityQuirk.FOUL_MOUTHED.description=%s curse%s profusely during combat, using colorful language\ + \ to express frustration, aggression, or even excitement. + +PersonalityQuirk.FREESTYLE_COMBAT.text=Uses Freestyle Combat Tactics +PersonalityQuirk.FREESTYLE_COMBAT.description=%s often use%s unconventional or freestyle combat tactics,\ + \ improvising as they go and surprising both allies and enemies. + +PersonalityQuirk.GEOMETRY_GURU.text=Calculates Firing Angles +PersonalityQuirk.GEOMETRY_GURU.description=%s meticulously calculate%s firing angles and ricochet paths,\ + \ often using math to gain an edge in complex combat situations. + +PersonalityQuirk.ICE_COLD.text=Emotionless in Combat +PersonalityQuirk.ICE_COLD.description=%s maintain%s a completely emotionless demeanor during combat,\ + \ often unnerving allies and making enemies question their sanity. + +PersonalityQuirk.PICKY_ABOUT_GEAR.text=Extremely Picky About Gear +PersonalityQuirk.PICKY_ABOUT_GEAR.description=%s are highly selective about their equipment, refusing\ + \ to use certain weapons or tech that doesn't meet their personal standards. + +PersonalityQuirk.RECORD_KEEPER.text=Detailed Battle Recorder +PersonalityQuirk.RECORD_KEEPER.description=%s meticulously record%s every battle detail, reviewing\ + \ logs and analyzing their performance with obsessive precision. + +PersonalityQuirk.RESOURCE_SCROUNGER.text=Resourceful Scrounger +PersonalityQuirk.RESOURCE_SCROUNGER.description=%s are skilled at scrounging for resources, finding\ + \ spare parts, ammo, and other useful items even in seemingly empty locations. + +PersonalityQuirk.TRASH_TALKER.text=Relentless Trash Talker +PersonalityQuirk.TRASH_TALKER.description=%s frequently trash-talk%s enemies during combat, trying to\ + \ throw them off balance with insults and psychological tactics. + +PersonalityQuirk.CORRECTS_PRONOUNS.text=Quick to Correct Pronouns +PersonalityQuirk.CORRECTS_PRONOUNS.description=%s promptly correct%s others when they use incorrect\ + \ pronouns, doing so confidently and without hesitation, as maintaining this respect is essential to their sense of identity. + +PersonalityQuirk.BODY_DISCOMFORT.text=Uncomfortable in Their Own Body +PersonalityQuirk.BODY_DISCOMFORT.description=%s frequently feel%s a deep sense of discomfort or\ + \ disconnect with their own body, often avoiding mirrors or tight-fitting clothing, and struggling\ + \ with a pervasive sense that their physical form doesn't match who they truly are. From 3ebdf5836bc10d240faab83ac7a2d268963e5752 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 19:25:24 -0500 Subject: [PATCH 090/118] Added Empty Jump Path Check `UnableToAffordJumpNagDialog` Prevented the 'UnableToAffordJumpNagDialog' nag from appearing if jump path is empty. --- .../nagDialogs/UnableToAffordJumpNagDialog.java | 4 ++++ .../UnableToAffordJumpNagDialogTest.java | 14 +++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java index df9648df95..bfc3424170 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java @@ -59,6 +59,10 @@ static boolean isUnableToAffordNextJump (Campaign campaign) { * @return the cost of the next jump for the campaign */ static Money getNextJumpCost(Campaign campaign) { + if (campaign.getLocation().getJumpPath() == null) { + return Money.zero(); + } + boolean isContractPayBasedOnToeUnitsValue = campaign.getCampaignOptions().isEquipmentContractBase(); return campaign.calculateCostPerJump(true, isContractPayBasedOnToeUnitsValue); diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java index 70cbe2cb4f..82c67cab51 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java @@ -20,6 +20,8 @@ import mekhq.campaign.Campaign; import mekhq.campaign.CampaignOptions; +import mekhq.campaign.CurrentLocation; +import mekhq.campaign.JumpPath; import mekhq.campaign.finances.Money; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -38,7 +40,6 @@ class UnableToAffordJumpNagDialogTest { // Mock objects for the tests private Campaign campaign; - private CampaignOptions options; /** * Test setup for each test, runs before each test. @@ -48,10 +49,17 @@ class UnableToAffordJumpNagDialogTest { void init() { // Initialize the mock objects campaign = mock(Campaign.class); - options = mock(CampaignOptions.class); + CampaignOptions options = mock(CampaignOptions.class); + CurrentLocation location = mock(CurrentLocation.class); + JumpPath jumpPath = mock(JumpPath.class); + // Stubs when(campaign.getCampaignOptions()).thenReturn(options); + + jumpPath.addSystem(campaign.getSystemById("Terra")); + when(campaign.getLocation()).thenReturn(location); + when(location.getJumpPath()).thenReturn(jumpPath); } // In the following tests the canAffordNextJump() method is called, and its response is checked @@ -72,4 +80,4 @@ void cannotAffordNextJump() { assertTrue(isUnableToAffordNextJump(campaign)); } -} \ No newline at end of file +} From 912bd3fe937bdf7ee946788ce9e7182317fffc30 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 19:38:40 -0500 Subject: [PATCH 091/118] Handle zero jump cost properly in UnableToAffordJumpNagDialog Added checks to handle scenarios where the jump cost is zero, preventing unnecessary dialogs. Ensured that the dialog does not display if the cost of the next jump is zero or if the current system is the last system on the jump path. --- .../UnableToAffordJumpNagDialog.java | 18 ++++++++++++++++-- .../UnableToAffordJumpNagDialogTest.java | 7 ++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java index bfc3424170..18a4ef9d30 100644 --- a/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialog.java @@ -21,10 +21,13 @@ import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; +import mekhq.campaign.CurrentLocation; +import mekhq.campaign.JumpPath; import mekhq.campaign.finances.Money; import mekhq.gui.baseComponents.AbstractMHQNagDialog; import javax.swing.*; +import java.util.Objects; /** * This class represents a nag dialog displayed when the campaign can't afford its next jump @@ -45,7 +48,11 @@ static boolean isUnableToAffordNextJump (Campaign campaign) { Money currentFunds = campaign.getFunds(); Money nextJumpCost = getNextJumpCost(campaign); - return currentFunds.isLessThan(nextJumpCost); + if (nextJumpCost.isZero()) { + return false; + } else { + return currentFunds.isLessThan(nextJumpCost); + } } /** @@ -59,7 +66,14 @@ static boolean isUnableToAffordNextJump (Campaign campaign) { * @return the cost of the next jump for the campaign */ static Money getNextJumpCost(Campaign campaign) { - if (campaign.getLocation().getJumpPath() == null) { + CurrentLocation location = campaign.getLocation(); + JumpPath jumpPath = location.getJumpPath(); + + if (jumpPath == null) { + return Money.zero(); + } + + if (Objects.equals(jumpPath.getLastSystem(), location.getCurrentSystem())) { return Money.zero(); } diff --git a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java index 82c67cab51..810ea982e5 100644 --- a/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java +++ b/MekHQ/unittests/mekhq/gui/dialog/nagDialogs/UnableToAffordJumpNagDialogTest.java @@ -23,6 +23,7 @@ import mekhq.campaign.CurrentLocation; import mekhq.campaign.JumpPath; import mekhq.campaign.finances.Money; +import mekhq.campaign.universe.PlanetarySystem; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -57,8 +58,12 @@ void init() { // Stubs when(campaign.getCampaignOptions()).thenReturn(options); - jumpPath.addSystem(campaign.getSystemById("Terra")); + PlanetarySystem originSystem = mock(PlanetarySystem.class); + PlanetarySystem destinationSystem = mock(PlanetarySystem.class); + + jumpPath.addSystem(destinationSystem); when(campaign.getLocation()).thenReturn(location); + when(location.getCurrentSystem()).thenReturn(originSystem); when(location.getJumpPath()).thenReturn(jumpPath); } From 5432cec56aec0a40bd4d6d629189591d7220adde Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 19:51:07 -0500 Subject: [PATCH 092/118] Add challenge level indicator to MissionViewPanel Introduced a new JPanel lblChallenge to display the mission's challenge level. Implemented the display of the contract's difficulty using skulls and adjusted the grid layout to accommodate the new component. --- MekHQ/src/mekhq/gui/view/MissionViewPanel.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/MekHQ/src/mekhq/gui/view/MissionViewPanel.java b/MekHQ/src/mekhq/gui/view/MissionViewPanel.java index c8051d18ca..e74c496aca 100644 --- a/MekHQ/src/mekhq/gui/view/MissionViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/MissionViewPanel.java @@ -49,6 +49,7 @@ public class MissionViewPanel extends JScrollablePanel { /* Basic Mission Parameters */ private JLabel lblStatus; + private JPanel lblChallenge; private JLabel lblLocation; private JLabel txtLocation; private JLabel lblType; @@ -562,6 +563,7 @@ private void fillStatsAtBContract() { // TODO : Switch me to use IUnitRating String[] ratingNames = {"F", "D", "C", "B", "A"}; lblStatus = new JLabel(); + lblChallenge = new JPanel(); lblLocation = new JLabel(); txtLocation = new JLabel(); lblEmployer = new JLabel(); @@ -612,6 +614,17 @@ private void fillStatsAtBContract() { gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; pnlStats.add(lblStatus, gridBagConstraints); + lblChallenge = contract.getContractDifficultySkulls(campaign); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = y++; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 0.0; + gridBagConstraints.insets = new Insets(0, 0, 5, 0); + gridBagConstraints.fill = GridBagConstraints.NONE; + gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; + pnlStats.add(lblChallenge, gridBagConstraints); lblLocation.setName("lblLocation"); lblLocation.setText(resourceMap.getString("lblLocation.text")); From 7731b8c91c8980f0dbdda8529bd3778cda432574 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 19:55:21 -0500 Subject: [PATCH 093/118] Fixed full skull size from 44x44 to 40x40 --- .../images/misc/challenge_estimate_full.png | Bin 2142 -> 2042 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/MekHQ/data/images/misc/challenge_estimate_full.png b/MekHQ/data/images/misc/challenge_estimate_full.png index e3e34095bf74be68f283beb4d05c29abca196c7e..53802ffd076cc881e6d406dbe9590014eeb5e0fc 100644 GIT binary patch literal 2042 zcmVPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91D4+uX1ONa40RR91C;$Ke0D9(TtN;K9WJyFpR9FeMnAvaCRTRfNOFK(D zQ%Y$ml%=$QSPF_t*a8^CQ=>tn#>AKy6XS#XlW+bD#up!miGd(7@}f3sBtdcMg9@~S zlCVet+uAl=Xgf=%Go8iHci^Wpzh&AX?MY6~`Q7b&?>Xn5duLn~4@PdLg{rEmnky?S zA9J}}o$w~upUdT3^rg@v=mT(1GMVh2pP#=*`&_x^wAf-ufIL2*uLVH6FuD!^HE=Dw zp6`d?mFQkAhMqxBqE4azgS^f6ST>s-WK+FUQ&Tr+%iL##Gc5*SAQ%j8bGzO9F|r4P zdyp+GS>?Q}ZI3_?Gye~Id(kgNB9Y(FV{p!P292cvs;@pA4tKKiXV4F^Xu|??=w6Tn zMxpKl?5S8R_9gllYzxvuhaCxAV|8`)tAKh3miNN6mD-TqI(BVZqY_>VZzpBu$j__j zw)yDbOSCKiIT&)WYE+S{ml2Y2LnsdY?L00Q_KK8%0B?sV%0KuDUdj=flb%!?5riN%m7t~ z`T;qDvO^>Vf;n7n{g?= zA4cv*Zh}|BBD!;9>2&%My5+jF1DM3{HNvbNHE9J%Z@V+Jo!|x1!*?96#?!kf54sMY zg3Tbcp%X;p3wV%zQJ5=qEAmD7dBCnhY9syFJdaGkMPv@4I_14M_v7Qp4X`|U36FgQ zxO3<;u)GoDYWf=^(*Qq=)WH%dlrF$cP!T=>oSs+mjKUZZSfvpCt;}gfYQ1j~iNsl? z1)yF2WVwq6F?xXsQMiJIV(>VsfEAL!E4f8U#wbhz6vJ?u?^$#KE~GRYOVFoU>p_mP zk#2Oo3oT>^up%0bM&j{!7o%@#2FB!Rb7HG2gr~P*M0eGi&j6 zyY)a&y_Ls$7}tk1Z=NRS6F5(9_0vB9D@WF_>y605JoE6D+lZlk7*djdOdadx0eS=d zbH=7H{1WT5q8p@5>WQ@(UJVZr@>7IY(Xl1F9Lx_S%nHFljJLB;J#r`BiW80@+FiA^ zwc7#kCc5%ZVf~E02r|dGD1Aqm(}6zD{XPOv58o~1-)r>gDc1<6$?e;`+I!iYtp~jf za-zbJ`T((m#Xby$LhYQmVfYAb2bg>pU_Z{y%_-S~V)Q9Mbphl(zTd~AN0BMO)dI2& zUEq{DY1WBMOiY;bRNZ+h1yBRrfG7moLYq9GQ&|uDFru613VH@FZDSKpqJKr`&Jl%I z&{YjuU>zu}hqy=REAa3mcjsP+2!mxhlnh%muVDpP%MCD~K-f6_x4!)nm zD@pQG{YL2#s{fIx=N>8S%4@R?qu=?(jDZxPyNW?o{=B!4TFh*y8Lxc+tE4Ezjp!=y z8n*^6B$Xtc+vmB-JCb9Wxj#dT==f`q1TS8_MDD`Mtx13ixIj(=;11uY6*@5TmfAIU zh`y8LR^EP99~s0&n5P-GI7|(Hc(fWpYXn13!SS!PD?O90#b1_Z9jB zuntBGdE5%Ph0>}BcsT$yQTOW+j9iAl!{DcUuY&i|Hx96FsE- z0vw7(dr#xb1*ka<7kvmfy61mCW@y1FLxdO&aCTV?71`dOsq zNky9@l?yQc(#9M6S<#D!sxhjkSOurMi*G$1egSwB9)6lJ`gx$ofu2%1HZzP|@L(|J zKPnfXcK0X_S*`OAO&9F*TT(gL@y!boHlk7fPF~EdzU^7m2-+USwE_7 z2Lge+fYL3o8de?Dn{NdyZ>@zDQneXx!CQW z3Bv1WGp;}_t~Dj)*$Mi)2=_6bQ(d&FhB}b}tmsBD8l5JUYWES&4i3f(gz_*0brlwp ziuLzw=4-s(kFGG6O^XGX_lV8vE>3fkfRbOj5{>n;fY!n2otc@@J-e(VqlV#SHDtH= Yzxz5ExzkhvM*si-07*qoM6N<$f*Z`L;{X5v literal 2142 zcmV-k2%-0hP)Px#4rN$LW=%~1DgXcg2mk;800000(o>TF00;g_L_t(o3B{RNY?M_L$2-%RPN$u= zlvGfGwk%}{B8z~95|*0agT@6DUrb0aMqhmN#RsB^CdT+=q6QO908PMz2W3e}5C}v_ z89-^Fv9)1e3YM+QblTbK??23^UuW(&of#>d>&^MXVVak&zJ>Ht!K zEXP0<J^lWDZ41gDMu&|Q0{@RQ5WB(e5JU!_>bP+-t+Y5OgRHCEiJ7F=rSxf z!E2aw4$`0VOrr#bJ5E>ajJ6! zIRGDpxAgb-A3zSnj-*eQjlAXM>&s#+& zppi0GF*G!EhtJVUNa)h?xJY*m<7t-xn1TI(J&PQATxglM52qRL6vnic$`ZyEVk}Cy zbRn-ZPU&YBTm_FsG4wjoxr!`B)}c@fr-z=tYzG^^&t%XDn1M=Zg9e3LwEf0}KWlQ* z&lKu94V&R5u<46w;g=X6g!jVjfC-?xkoMI8UyiJYEgFo)Vh6+F@ZVO0U8b)t*@ux5 z4AxVb3v0(}6WnI99rS%ZFfee0v;GE`l)vDkUa$9J%41qUI_L-hzlT5LwQ(H1n*h3s z-YMp2!KNWE&p4inR2Sx@u#NJ+uemIAg=u(IReAHvxrHtHuRhhQT) z0%r6Om+3Z)U8k&1`4m9vkm=V=A74XYS21B1W4<6(TM3;m7QB^qecnyn4VsYl1*@zF z>r7Y+x`FPu=v|@gNXAWPjb>B$S@8?h<$-DdJ;MeYU_;1gyt1;g3b3y+;Rmo5a{!%f z!^6Y!& zi6GwH#i3=qLs*KA)=~ppWCCrD)v$i#6JUW~122ZNem?q*gspCD?T1g})e&SEuzzx3 zXb))5IMP^vO^RNAx_Xh058(x{cDEj^xscM;B|z>aHE!oVaV{4n`tR#=%t2aOh`8{iSZeuI81G776dt4HY{Mfn1D19(1o z6wmqzwS}_6VlLbc>o?$^Xq$xm5viY66-Moh)u;b~l(m(O*~Eeyk!^TcStMt2N-<6b z5;}4LqZ9BV2ZL@__uyYqXn}iZ?*N!ixEQR>bOw)Zr*9W}myz=LJTgcJxjYhSx3lBKxDsA%KfWMLoo+UcdU6 z)-5c*`Lj?hrB6o~?TpK%>yql^*pcZKd zs9{NBj6yaxz+_XpT*^BH}7%(0FeoF U3w%u6)Bpeg07*qoM6N<$f)ttW$N&HU From 60200031e601541bab2a8fd72490e7ce362f6f4c Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 20:25:23 -0500 Subject: [PATCH 094/118] Fixed Camouflage Directory Assignment for Null Faction Codes Changed the default camouflage directory to "Standard Camouflage" when the faction code is null, ensuring valid directory assignment. --- .../src/mekhq/campaign/mission/AtBContract.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 81eb316b13..656b435565 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -43,7 +43,6 @@ import mekhq.campaign.mission.atb.AtBScenarioFactory; import mekhq.campaign.mission.enums.AtBContractType; import mekhq.campaign.mission.enums.AtBMoraleLevel; -import mekhq.campaign.mission.enums.ContractCommandRights; import mekhq.campaign.personnel.Bloodname; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.backgrounds.BackgroundsController; @@ -74,7 +73,6 @@ import java.time.LocalDate; import java.util.List; import java.util.*; -import java.util.stream.Collectors; import static java.lang.Math.round; import static megamek.client.ratgenerator.ModelRecord.NETWORK_NONE; @@ -247,15 +245,19 @@ public static Camouflage pickRandomCamouflage(int currentYear, String factionCod // Define the root directory and get the faction-specific camouflage directory final String ROOT_DIRECTORY = "data/images/camo/"; - String camouflageDirectory = getCamouflageDirectory(currentYear, factionCode); + String camouflageDirectory = "Standard Camouflage"; + + if (factionCode != null) { + camouflageDirectory = getCamouflageDirectory(currentYear, factionCode); + } // Gather all files List allPaths = null; try { allPaths = Files.find(Paths.get(ROOT_DIRECTORY + camouflageDirectory + '/'), Integer.MAX_VALUE, - (path, bfa) -> {return bfa.isRegularFile();}) - .collect(Collectors.toList()); + (path, bfa) -> bfa.isRegularFile()) + .toList(); } catch (IOException e) { logger.error("Error getting list of camouflages", e); } @@ -266,7 +268,7 @@ public static Camouflage pickRandomCamouflage(int currentYear, String factionCod String fileName = randomPath.getFileName().toString(); String fileCategory = randomPath.getParent().toString() - .replaceAll("\\\\", "/"); // Is this necessary on windows machines? + .replaceAll("\\\\", "/"); // This is necessary for windows machines fileCategory = fileCategory.replaceAll(ROOT_DIRECTORY, ""); return new Camouflage(fileCategory, fileName); @@ -1934,7 +1936,7 @@ private static double estimateMekStrength(Campaign campaign, String factionCode, // getMekSummary(int index) is NULL for salvage. int genericBattleValue = unitTable.getMekSummary(i).loadEntity().getGenericBattleValue(); int weight = unitTable.getEntryWeight(i); // NOT 0 for salvage - + totalBattleValue += battleValue * weight; totalGBV += genericBattleValue * weight; rollingCount += weight; From c3259999d0073d04b675fb95dcebc6f21a41c427 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 20:28:17 -0500 Subject: [PATCH 095/118] Added Minimum Number of Tracks (1) to StratCon Initialization Changed the calculation for `numTracks` to use Math.max, ensuring it is at least 1. This prevents issues that arose from having zero tracks. --- .../stratcon/StratconContractInitializer.java | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 303564e5e3..53da4fce77 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -18,25 +18,21 @@ */ package mekhq.campaign.stratcon; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import megamek.common.Compute; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; import mekhq.campaign.force.Force; -import mekhq.campaign.mission.AtBContract; -import mekhq.campaign.mission.AtBDynamicScenario; -import mekhq.campaign.mission.Mission; -import mekhq.campaign.mission.Scenario; +import mekhq.campaign.mission.*; import mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment; -import mekhq.campaign.mission.ScenarioTemplate; import mekhq.campaign.mission.atb.AtBScenarioModifier; import mekhq.campaign.mission.enums.ContractCommandRights; import mekhq.campaign.stratcon.StratconContractDefinition.ObjectiveParameters; import mekhq.campaign.stratcon.StratconContractDefinition.StrategicObjectiveType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * This class handles StratCon state initialization when a contract is signed. */ @@ -72,7 +68,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // scenarios // when objective is allied/hostile facility, place those facilities - int numTracks = contract.getRequiredLances() / NUM_LANCES_PER_TRACK; + int numTracks = Math.max(1, contract.getRequiredLances() / NUM_LANCES_PER_TRACK); int planetaryTemperature = campaign.getLocation().getPlanet().getTemperature(campaign.getLocalDate()); for (int x = 0; x < numTracks; x++) { @@ -402,12 +398,11 @@ private static StratconCoords getUnoccupiedCoords(StratconTrackState trackState) * such as pointers from StratCon scenario objects to AtB scenario objects. */ public static void restoreTransientStratconInformation(Mission m, Campaign campaign) { - if (m instanceof AtBContract) { + if (m instanceof AtBContract atbContract) { // Having loaded scenarios and such, we now need to go through any StratCon // scenarios for this contract // and set their backing scenario pointers to the existing scenarios stored in // the campaign for this contract - AtBContract atbContract = (AtBContract) m; if (atbContract.getStratconCampaignState() != null) { for (StratconTrackState track : atbContract.getStratconCampaignState().getTracks()) { for (StratconScenario scenario : track.getScenarios().values()) { From 6aba2eea16396e128cfa442158dddb9b0b6d30e4 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 20:29:44 -0500 Subject: [PATCH 096/118] Added Minimum Number of Tracks (1) to StratCon Initialization Changed the calculation for `numTracks` to use Math.max, ensuring it is at least 1. This prevents issues that arose from having zero tracks. --- .../stratcon/StratconContractInitializer.java | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 303564e5e3..53da4fce77 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -18,25 +18,21 @@ */ package mekhq.campaign.stratcon; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - import megamek.common.Compute; import megamek.logging.MMLogger; import mekhq.campaign.Campaign; import mekhq.campaign.force.Force; -import mekhq.campaign.mission.AtBContract; -import mekhq.campaign.mission.AtBDynamicScenario; -import mekhq.campaign.mission.Mission; -import mekhq.campaign.mission.Scenario; +import mekhq.campaign.mission.*; import mekhq.campaign.mission.ScenarioForceTemplate.ForceAlignment; -import mekhq.campaign.mission.ScenarioTemplate; import mekhq.campaign.mission.atb.AtBScenarioModifier; import mekhq.campaign.mission.enums.ContractCommandRights; import mekhq.campaign.stratcon.StratconContractDefinition.ObjectiveParameters; import mekhq.campaign.stratcon.StratconContractDefinition.StrategicObjectiveType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** * This class handles StratCon state initialization when a contract is signed. */ @@ -72,7 +68,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // scenarios // when objective is allied/hostile facility, place those facilities - int numTracks = contract.getRequiredLances() / NUM_LANCES_PER_TRACK; + int numTracks = Math.max(1, contract.getRequiredLances() / NUM_LANCES_PER_TRACK); int planetaryTemperature = campaign.getLocation().getPlanet().getTemperature(campaign.getLocalDate()); for (int x = 0; x < numTracks; x++) { @@ -402,12 +398,11 @@ private static StratconCoords getUnoccupiedCoords(StratconTrackState trackState) * such as pointers from StratCon scenario objects to AtB scenario objects. */ public static void restoreTransientStratconInformation(Mission m, Campaign campaign) { - if (m instanceof AtBContract) { + if (m instanceof AtBContract atbContract) { // Having loaded scenarios and such, we now need to go through any StratCon // scenarios for this contract // and set their backing scenario pointers to the existing scenarios stored in // the campaign for this contract - AtBContract atbContract = (AtBContract) m; if (atbContract.getStratconCampaignState() != null) { for (StratconTrackState track : atbContract.getStratconCampaignState().getTracks()) { for (StratconScenario scenario : track.getScenarios().values()) { From e3fd3b77266199e4452215d848831595dd5965a1 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 20:31:14 -0500 Subject: [PATCH 097/118] Rolled back push to wrong branch --- .../mekhq/campaign/stratcon/StratconContractInitializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 53da4fce77..7aa08e9068 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -68,7 +68,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // scenarios // when objective is allied/hostile facility, place those facilities - int numTracks = Math.max(1, contract.getRequiredLances() / NUM_LANCES_PER_TRACK); + int numTracks = contract.getRequiredLances() / NUM_LANCES_PER_TRACK; int planetaryTemperature = campaign.getLocation().getPlanet().getTemperature(campaign.getLocalDate()); for (int x = 0; x < numTracks; x++) { From 8c1bed87402fa664d4c06aae6e54c836d1bf3447 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 20:33:10 -0500 Subject: [PATCH 098/118] Fixed Objective Criteria for `Intercept Engagement` Raised the destruction or routing requirement from 50% to 75% for scenario completion. --- MekHQ/data/scenariotemplates/Intercept Engagement.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MekHQ/data/scenariotemplates/Intercept Engagement.xml b/MekHQ/data/scenariotemplates/Intercept Engagement.xml index d907fe595f..2f7b5460a1 100644 --- a/MekHQ/data/scenariotemplates/Intercept Engagement.xml +++ b/MekHQ/data/scenariotemplates/Intercept Engagement.xml @@ -128,10 +128,10 @@ - Destroy or rout 50% the following force(s) and unit(s): + Destroy or rout 75% the following force(s) and unit(s): NONE Destroy - 50 + 75 0 true None From 8732863682459e514cc153107b2d2b7aab05c106 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 22:10:06 -0500 Subject: [PATCH 099/118] Corrected Parameter Order in Simulated Relationship History Reports Children are no longer considered the parents of their parents. --- MekHQ/src/mekhq/campaign/Campaign.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index ac638a50d4..671c6d5ad6 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -1752,8 +1752,8 @@ private void simulateRelationshipHistory(Person person) { recruitPerson(currentSpouse, PrisonerStatus.FREE, true, false); addReport(String.format(resources.getString("relativeJoinsForce.text"), - person.getHyperlinkedFullTitle(), currentSpouse.getHyperlinkedFullTitle(), + person.getHyperlinkedFullTitle(), resources.getString("relativeJoinsForceSpouse.text"))); MekHQ.triggerEvent(new PersonChangedEvent(currentSpouse)); @@ -1799,8 +1799,8 @@ private void simulateRelationshipHistory(Person person) { recruitPerson(child, PrisonerStatus.FREE, true, false); addReport(String.format(resources.getString("relativeJoinsForce.text"), - person.getHyperlinkedFullTitle(), child.getHyperlinkedFullTitle(), + person.getHyperlinkedFullTitle(), resources.getString("relativeJoinsForceChild.text"))); MekHQ.triggerEvent(new PersonChangedEvent(child)); @@ -2250,13 +2250,11 @@ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { if (ignoreMothballedUnits && (null != incomingPart.getUnit()) && incomingPart.getUnit().isMothballed()) { - return; } else if ((incomingPart.getUnit() != null) || (incomingPart instanceof MissingPart)) { partInUse.setUseCount(partInUse.getUseCount() + getQuantity(incomingPart)); } else { if (incomingPart.isPresent()) { if (incomingPart.getQuality().toNumeric() < ignoreSparesUnderQuality.toNumeric()) { - return; } else { partInUse.setStoreCount(partInUse.getStoreCount() + getQuantity(incomingPart)); partInUse.addSpare(incomingPart); @@ -2273,7 +2271,7 @@ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, * @param ignoreMothballedUnits don't count parts in mothballed units * @param ignoreSparesUnderQuality don't count spare parts lower than this quality */ - public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, + public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { partInUse.setUseCount(0); partInUse.setStoreCount(0); @@ -2290,7 +2288,7 @@ public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartInUse newPiu = getPartInUse((Part) maybePart); if (partInUse.equals(newPiu)) { partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? + + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() : (Part) maybePart) * maybePart.getQuantity()); } @@ -2334,7 +2332,7 @@ public Set getPartsInUse(boolean ignoreMothballedUnits, inUse.put(partInUse, partInUse); } partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? + + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() : (Part) maybePart) * maybePart.getQuantity()); @@ -8002,7 +8000,7 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT // } break; } - if (p.getQuality().toNumeric() > oldQuality.toNumeric()) { + if (p.getQuality().toNumeric() > oldQuality.toNumeric()) { partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor( MekHQ.getMHQOptions().getFontColorPositiveHexColor(), "new quality is " + p.getQualityName()); From 8eaade6be7cbf6d70b1340f7a585281c141b28b2 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 22:59:09 -0500 Subject: [PATCH 100/118] Simplified Reputation Display Displaying Reputation and AtB Modifier was causing confusion. Especially as the AtB Modifier isn't a consistent value, with it simply being a base modifier that is further modified by the individual AtB legacy systems. This PR removes the AtB modifier from the Reputation display. --- MekHQ/src/mekhq/campaign/Campaign.java | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index ac638a50d4..43b42c6f73 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -2250,13 +2250,11 @@ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { if (ignoreMothballedUnits && (null != incomingPart.getUnit()) && incomingPart.getUnit().isMothballed()) { - return; } else if ((incomingPart.getUnit() != null) || (incomingPart instanceof MissingPart)) { partInUse.setUseCount(partInUse.getUseCount() + getQuantity(incomingPart)); } else { if (incomingPart.isPresent()) { if (incomingPart.getQuality().toNumeric() < ignoreSparesUnderQuality.toNumeric()) { - return; } else { partInUse.setStoreCount(partInUse.getStoreCount() + getQuantity(incomingPart)); partInUse.addSpare(incomingPart); @@ -2273,7 +2271,7 @@ private void updatePartInUseData(PartInUse partInUse, Part incomingPart, * @param ignoreMothballedUnits don't count parts in mothballed units * @param ignoreSparesUnderQuality don't count spare parts lower than this quality */ - public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, + public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartQuality ignoreSparesUnderQuality) { partInUse.setUseCount(0); partInUse.setStoreCount(0); @@ -2290,7 +2288,7 @@ public void updatePartInUse(PartInUse partInUse, boolean ignoreMothballedUnits, PartInUse newPiu = getPartInUse((Part) maybePart); if (partInUse.equals(newPiu)) { partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? + + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() : (Part) maybePart) * maybePart.getQuantity()); } @@ -2334,7 +2332,7 @@ public Set getPartsInUse(boolean ignoreMothballedUnits, inUse.put(partInUse, partInUse); } partInUse.setPlannedCount(partInUse.getPlannedCount() - + getQuantity((maybePart instanceof MissingPart) ? + + getQuantity((maybePart instanceof MissingPart) ? ((MissingPart) maybePart).getNewPart() : (Part) maybePart) * maybePart.getQuantity()); @@ -6950,10 +6948,7 @@ public String getUnitRatingText() { if (unitRatingMethod.isFMMR()) { return getUnitRating().getUnitRating(); } else if (unitRatingMethod.isCampaignOperations()) { - int reputationRating = reputation.getReputationRating(); - int unitRatingMod = getAtBUnitRatingMod(); - - return String.format("%d (%+d)", reputationRating, unitRatingMod); + return String.valueOf(reputation.getReputationRating()); } else { return "N/A"; } @@ -8002,7 +7997,7 @@ private String doMaintenanceOnUnitPart(Unit u, Part p, Map partsT // } break; } - if (p.getQuality().toNumeric() > oldQuality.toNumeric()) { + if (p.getQuality().toNumeric() > oldQuality.toNumeric()) { partReport += ": " + ReportingUtilities.messageSurroundedBySpanWithColor( MekHQ.getMHQOptions().getFontColorPositiveHexColor(), "new quality is " + p.getQualityName()); From 4c28b053cf80254b412b9f97e9e325ccd25ec61a Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 23:37:04 -0500 Subject: [PATCH 101/118] Refactored logging for entity generation failures Updated log statements to use clearer method calls for unit type and weight class names. Moved a log statement inside the appropriate condition block for better readability and context. --- .../campaign/mission/AtBDynamicScenarioFactory.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java index f679304fbe..e6efb943a3 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java @@ -2901,7 +2901,8 @@ private static List generateLance(String faction, SkillLevel skill, int if (newEntity == null) { logger.info(String.format("Failed to generate unit of type %s, weight %s. Beginning substitution.", - unitTypes.get(i), AtBConfiguration.decodeWeightStr(weights, i))); + UnitType.getTypeName(unitTypes.get(i)), + EntityWeightClass.getClassName(AtBConfiguration.decodeWeightStr(weights, i)))); // If we've failed to get an entity, we start adjusting weight categories to see // if we hit something valid. @@ -2917,16 +2918,16 @@ private static List generateLance(String faction, SkillLevel skill, int newEntity = getNewEntity(faction, skill, quality, individualType, weight, individualRole, campaign, 0); if (newEntity != null) { - logger.info(String.format("Substitution successful. Substituted with %s.", - weight)); + logger.info(String.format("Substitution successful (%s)", + EntityWeightClass.getClassName(AtBConfiguration.decodeWeightStr(weights, i)))); break; } } // If we still haven't got a valid entity, use hardcoded fallbacks. - logger.info("Substitution unsuccessful. Using hardcoded fallbacks"); - if (newEntity == null) { + logger.info("Substitution unsuccessful. Using hardcoded fallbacks"); + if (unitTypes.get(0) == UnitType.DROPSHIP) { newEntity = getNewEntity(faction, skill, quality, List.of(UnitType.DROPSHIP), weights, Map.of(UnitType.DROPSHIP, List.of(CIVILIAN)), From c0055ac5c0e60e87644e55d1f78977277a972d2c Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 20 Oct 2024 23:47:51 -0500 Subject: [PATCH 102/118] More FG3 Scenario Adjustments Updated force multipliers in several scenario templates to lower values to ensure more balanced gameplay. Lowered weight of OpFor in `Irregular Force Assault` from 2-4 to 1-3. Removed the generic OpFor from `Irregular Force Suppression` to make these two scenarios feel more distinctive. --- .../Frontline Disruption.xml | 2 +- .../Irregular Force Assault.xml | 8 ++--- .../Irregular Force Suppression.xml | 34 ++----------------- .../scenariotemplates/Skirmish Disruption.xml | 2 +- .../scenariotemplates/Tactical Withdrawal.xml | 2 +- 5 files changed, 9 insertions(+), 39 deletions(-) diff --git a/MekHQ/data/scenariotemplates/Frontline Disruption.xml b/MekHQ/data/scenariotemplates/Frontline Disruption.xml index ab2cb87a4e..433075d005 100644 --- a/MekHQ/data/scenariotemplates/Frontline Disruption.xml +++ b/MekHQ/data/scenariotemplates/Frontline Disruption.xml @@ -63,7 +63,7 @@ 6 0 2 - 2.0 + 1.5 OpFor 1 5 diff --git a/MekHQ/data/scenariotemplates/Irregular Force Assault.xml b/MekHQ/data/scenariotemplates/Irregular Force Assault.xml index 24b75e8871..32bd6d1cf3 100644 --- a/MekHQ/data/scenariotemplates/Irregular Force Assault.xml +++ b/MekHQ/data/scenariotemplates/Irregular Force Assault.xml @@ -79,8 +79,8 @@ OpFor 1 5 - 4 - 2 + 3 + 1 50 0 @@ -107,7 +107,7 @@ 5 0 2 - 2.0 + 1.0 Infantry 2 5 @@ -138,7 +138,7 @@ 5 0 2 - 2.0 + 1.0 Irregulars 2 5 diff --git a/MekHQ/data/scenariotemplates/Irregular Force Suppression.xml b/MekHQ/data/scenariotemplates/Irregular Force Suppression.xml index 573c718644..a5c03ea141 100644 --- a/MekHQ/data/scenariotemplates/Irregular Force Suppression.xml +++ b/MekHQ/data/scenariotemplates/Irregular Force Suppression.xml @@ -59,36 +59,6 @@ false - - OpFor - - -1 - false - -2 - 0 - true - false - true - false - false - - 5 - -1 - 2 - 1.0 - OpFor - 1 - 5 - 4 - 0 - - 50 - 0 - OppositeEdge - Player - false - - Infantry @@ -107,7 +77,7 @@ 5 0 2 - 2.0 + 1.0 Infantry 2 5 @@ -138,7 +108,7 @@ 5 0 2 - 2.0 + 1.0 Irregulars 2 5 diff --git a/MekHQ/data/scenariotemplates/Skirmish Disruption.xml b/MekHQ/data/scenariotemplates/Skirmish Disruption.xml index 4615367b98..8675f05d72 100644 --- a/MekHQ/data/scenariotemplates/Skirmish Disruption.xml +++ b/MekHQ/data/scenariotemplates/Skirmish Disruption.xml @@ -63,7 +63,7 @@ 6 0 2 - 2.0 + 1.5 OpFor 1 5 diff --git a/MekHQ/data/scenariotemplates/Tactical Withdrawal.xml b/MekHQ/data/scenariotemplates/Tactical Withdrawal.xml index 128fd29f20..d07d622fa5 100644 --- a/MekHQ/data/scenariotemplates/Tactical Withdrawal.xml +++ b/MekHQ/data/scenariotemplates/Tactical Withdrawal.xml @@ -126,7 +126,7 @@ 5 0 2 - 2.0 + 1.5 OpFor 1 5 From b246b969a2d7381e6d271be94591d32d393e972b Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Mon, 21 Oct 2024 00:09:45 -0500 Subject: [PATCH 103/118] Added Award Bonuses to Award Ceremony Dialog Added logic to include award bonus details like XP and Edge rewards in the award description in the autoAwards award ceremony dialog. --- .../campaign/personnel/enums/AwardBonus.java | 2 +- .../mekhq/gui/model/AutoAwardsTableModel.java | 95 ++++++++++--------- 2 files changed, 53 insertions(+), 44 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/AwardBonus.java b/MekHQ/src/mekhq/campaign/personnel/enums/AwardBonus.java index 21fb08a8d5..2dc18f0589 100644 --- a/MekHQ/src/mekhq/campaign/personnel/enums/AwardBonus.java +++ b/MekHQ/src/mekhq/campaign/personnel/enums/AwardBonus.java @@ -27,7 +27,7 @@ public enum AwardBonus { BOTH("AwardBonuses.BOTH.text", "AwardBonuses.BOTH.toolTipText"), XP("AwardBonuses.XP.text", "AwardBonuses.XP.toolTipText"), EDGE("AwardBonuses.EDGE.text", "AwardBonuses.EDGE.toolTipText"), - NONE("AwardBonuses.NONE.text", "AwardBonuses.NONE.toolTipText"),; + NONE("AwardBonuses.NONE.text", "AwardBonuses.NONE.toolTipText"); //endregion Enum Declarations //region Variable Declarations diff --git a/MekHQ/src/mekhq/gui/model/AutoAwardsTableModel.java b/MekHQ/src/mekhq/gui/model/AutoAwardsTableModel.java index 8e514d5450..14f78dd501 100644 --- a/MekHQ/src/mekhq/gui/model/AutoAwardsTableModel.java +++ b/MekHQ/src/mekhq/gui/model/AutoAwardsTableModel.java @@ -18,24 +18,23 @@ */ package mekhq.gui.model; -import java.awt.Component; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import javax.swing.JTable; -import javax.swing.SwingConstants; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableCellRenderer; - import megamek.logging.MMLogger; import mekhq.campaign.Campaign; import mekhq.campaign.personnel.Award; import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.enums.AwardBonus; import mekhq.gui.BasicInfo; import mekhq.gui.utilities.MekHqTableCellRenderer; +import javax.swing.*; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableCellRenderer; +import java.awt.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + public class AutoAwardsTableModel extends AbstractTableModel { private static final MMLogger logger = MMLogger.create(AutoAwardsTableModel.class); @@ -85,28 +84,19 @@ public String getColumnName(int column) { } public int getColumnWidth(int column) { - switch (column) { - case COL_PERSON: - case COL_NAME: - return 75; - case COL_SET: - return 40; - case COL_DESCRIPTION: - return 400; - case COL_AWARD: - default: - return 30; - } + return switch (column) { + case COL_PERSON, COL_NAME -> 75; + case COL_SET -> 40; + case COL_DESCRIPTION -> 400; + default -> 30; + }; } public int getAlignment(int column) { - switch (column) { - case COL_PERSON: - case COL_DESCRIPTION: - return SwingConstants.LEFT; - default: - return SwingConstants.CENTER; - } + return switch (column) { + case COL_PERSON, COL_DESCRIPTION -> SwingConstants.LEFT; + default -> SwingConstants.CENTER; + }; } @Override @@ -142,20 +132,39 @@ public Object getValueAt(int rowIndex, int columnIndex) { Person person = campaign.getPerson(personUUID); Award award = (Award) rowData.get(1); - switch (columnIndex) { - case COL_PERSON: - return person.makeHTMLRank(); - case COL_NAME: - return award.getName(); - case COL_SET: - return award.getSet(); - case COL_AWARD: - return rowData.get(2); - case COL_DESCRIPTION: - return award.getDescription(); - default: - return "?"; + return switch (columnIndex) { + case COL_PERSON -> person.makeHTMLRank(); + case COL_NAME -> award.getName(); + case COL_SET -> award.getSet(); + case COL_AWARD -> rowData.get(2); + case COL_DESCRIPTION -> { + String awards = getDescriptionString(award); + + yield award.getDescription() + awards; + } + default -> "?"; + }; + } + + /** + * Retrieves a description for the given award based on the campaign's award bonus style. + * + * @param award The {@link Award} object for which the description string is generated. + * @return A {@link String} containing the awards based on the style, including XP and Edge rewards if applicable. + */ + private String getDescriptionString(Award award) { + AwardBonus style = campaign.getCampaignOptions().getAwardBonusStyle(); + int xpAward = award.getXPReward(); + int edgeAward = award.getEdgeReward(); + + String awards = ""; + if (style.isBoth() || style.isXP()) { + awards += (xpAward > 0) ? " (" + xpAward + "XP)" : ""; + } + if (style.isBoth() || style.isEdge()) { + awards += (edgeAward > 0) ? " (" + edgeAward + " Edge)" : ""; } + return awards; } @Override From c0adb0c8115711cdec162bd5be9a37362d5cc3cc Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 21 Oct 2024 07:58:46 -0700 Subject: [PATCH 104/118] PartsStore - Fix EquipmentParts and OmniPods --- MekHQ/src/mekhq/campaign/market/PartsStore.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/market/PartsStore.java b/MekHQ/src/mekhq/campaign/market/PartsStore.java index 784b99e626..bee9da649b 100644 --- a/MekHQ/src/mekhq/campaign/market/PartsStore.java +++ b/MekHQ/src/mekhq/campaign/market/PartsStore.java @@ -232,6 +232,8 @@ private void stockWeaponsAmmoAndEquipment(Campaign c) { epart = new EquipmentPart(0, et, -1, 1.0, true, c); epart.setEquipTonnage(ton); parts.add(epart); + epart = new EquipmentPart(0, et, -1, 1.0, true, c); + epart.setEquipTonnage(ton); parts.add(new OmniPod(epart, c)); } // TODO: still need to deal with talons (unit tonnage) and masc (engine rating) @@ -241,7 +243,7 @@ private void stockWeaponsAmmoAndEquipment(Campaign c) { parts.add(p); if (poddable) { parts.add(new EquipmentPart(0, et, -1, 1.0, true, c)); - parts.add(new OmniPod(p, c)); + parts.add(new OmniPod(new EquipmentPart(0, et, -1, 1.0, false, c), c)); } } } From 6fb350f9ceb39a77631cec92877e68a3782623d3 Mon Sep 17 00:00:00 2001 From: algebro Date: Fri, 18 Oct 2024 09:57:11 -0400 Subject: [PATCH 105/118] add static hiring hall data --- MekHQ/data/universe/systems.xml | 52 +++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/MekHQ/data/universe/systems.xml b/MekHQ/data/universe/systems.xml index df7a6b4621..457722a701 100644 --- a/MekHQ/data/universe/systems.xml +++ b/MekHQ/data/universe/systems.xml @@ -35472,6 +35472,10 @@ A few Stone Age tribes exist in the planet's deep deserts and jungles, far from 281.417 K4IV 3 + + 2694-01-01 + QUESTIONABLE + Riverhead Giant Terrestrial @@ -40713,6 +40717,10 @@ Arboris has a history of fierce independence. In 2308, Arboris seceded from the 226.576 G3V 4 + + 3057-01-01 + STANDARD + Plowden's Stand Dwarf Terrestrial @@ -48861,6 +48869,10 @@ Though a major exporter of heavy metals and radioactive elements, as well as sma -315.408 G4V 3 + + 2912-01-01 + QUESTIONABLE + Frey Dwarf Terrestrial @@ -221714,6 +221726,11 @@ Coalition Armory -23.569 G4V 7 + + 3058-01-01 + 3081-03-15 + MINOR + Csurgói Járás Terrestrial @@ -230631,6 +230648,10 @@ At the dawn the thirty-second century, a surgical strike from the Lyran Commonwe 34.077 F8II 6 + + 2650-01-01 + GREAT + Skouzas's Frontier Terrestrial @@ -280553,6 +280574,10 @@ Freeport Armorworks -430.11 G3IV 2 + + 3020-01-01 + MINOR + Mugoma Giant Terrestrial @@ -373827,6 +373852,11 @@ Aside from the large island continent of Galapagos in the northern hemisphere of 18.309 G0III 4 + + 2811-01-01 + 3045-01-01 + MINOR + Port de Nedelec Terrestrial @@ -480796,6 +480826,10 @@ Niops V and VI were settled primarily to expand the available resources to the N 148.073 K0V 3 + + 3052-01-01 + MINOR + Kaarst Terrestrial @@ -482463,6 +482497,11 @@ During the Fourth Succession War, the Federated Suns leaked false reports of ung -2.891 G2IV 2 + + 3057-01-01 + 3081-03-15 + GREAT + Bloomsburg Giant Terrestrial @@ -498966,6 +499005,11 @@ Rim Motors -34.688 K9V 2 + + 3031-01-01 + 3067-10-15 + GREAT + Chen Dwarf Terrestrial @@ -601689,6 +601733,10 @@ During the Jihad, the Word of Blake forces invaded after they neutralized the de -7.025 K1V 7 + + 2700-01-01 + MINOR + Sanopi Dwarf Terrestrial @@ -701566,6 +701614,10 @@ Wei is home to one of the many Duchy RTC training facilities in the Confederatio -276.084 G3V 2 + + 3000-01-01 + GREAT + High Kelling Giant Terrestrial From aace0e03f0f11b1b717163f6e7ac3b3c161da464 Mon Sep 17 00:00:00 2001 From: algebro Date: Fri, 18 Oct 2024 11:15:39 -0400 Subject: [PATCH 106/118] add hiring hall override class --- .../campaign/universe/HiringHallOverride.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java diff --git a/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java b/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java new file mode 100644 index 0000000000..dc44ed777d --- /dev/null +++ b/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.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.campaign.universe; + +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import mekhq.adapter.LocalDateAdapter; +import mekhq.campaign.universe.enums.HiringHallLevel; + +import java.time.LocalDate; + +@XmlRootElement(name = "hiringHall") +@XmlAccessorType(value = XmlAccessType.FIELD) +public class HiringHallOverride { + @XmlJavaTypeAdapter(value = LocalDateAdapter.class) + private LocalDate start; + @XmlJavaTypeAdapter(value = LocalDateAdapter.class) + private LocalDate end; + @XmlElement(name = "level", required = false) + private HiringHallLevel level; + + + public LocalDate getStart() { + return start; + } + + public void setStart(LocalDate start) { + this.start = start; + } + + public LocalDate getEnd() { + return end; + } + + public void setEnd(LocalDate end) { + this.end = end; + } + + public HiringHallLevel getLevel() { + return level; + } + + public void setLevel(HiringHallLevel level) { + this.level = level; + } + + public boolean isActive(LocalDate date) { + return date.isAfter(start) && date.isBefore(end); + } +} From 582a85c5a58244e6563f1bca4f79e60caf64c4e6 Mon Sep 17 00:00:00 2001 From: algebro Date: Fri, 18 Oct 2024 11:15:53 -0400 Subject: [PATCH 107/118] add local date adapter --- MekHQ/src/mekhq/adapter/LocalDateAdapter.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 MekHQ/src/mekhq/adapter/LocalDateAdapter.java diff --git a/MekHQ/src/mekhq/adapter/LocalDateAdapter.java b/MekHQ/src/mekhq/adapter/LocalDateAdapter.java new file mode 100644 index 0000000000..323332cc24 --- /dev/null +++ b/MekHQ/src/mekhq/adapter/LocalDateAdapter.java @@ -0,0 +1,35 @@ +/* + * 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.adapter; + +import jakarta.xml.bind.annotation.adapters.XmlAdapter; + +import java.time.LocalDate; + +public class LocalDateAdapter extends XmlAdapter { + @Override + public LocalDate unmarshal(String v) throws Exception { + return LocalDate.parse(v); + } + + @Override + public String marshal(LocalDate v) throws Exception { + return v.toString(); + } +} From 69bd29b541edf03b87a4965e36a376d77cf2d9e1 Mon Sep 17 00:00:00 2001 From: algebro Date: Fri, 18 Oct 2024 11:16:16 -0400 Subject: [PATCH 108/118] WIP: add static hiring hall overrides to PlanetarySystem --- MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java b/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java index 042017ed94..db83243601 100644 --- a/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java +++ b/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java @@ -133,6 +133,9 @@ public class PlanetarySystem { // the location of the primary planet for this system private int primarySlot; + @XmlElement(name = "hiringHall", required = false) + private HiringHallOverride staticHall = null; + /** Marker for "please delete this system" */ @XmlJavaTypeAdapter(value = BooleanValueAdapter.class) public Boolean delete; From e361722feef9919895334bdf78f7e3bca6ebdf7b Mon Sep 17 00:00:00 2001 From: algebro Date: Sat, 19 Oct 2024 13:47:07 -0400 Subject: [PATCH 109/118] extract some logic out of getHiringHallLevel() --- .../mekhq/campaign/universe/PlanetarySystem.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java b/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java index db83243601..768594bcad 100644 --- a/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java +++ b/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java @@ -133,7 +133,7 @@ public class PlanetarySystem { // the location of the primary planet for this system private int primarySlot; - @XmlElement(name = "hiringHall", required = false) + @XmlElement(name = "hiringHall") private HiringHallOverride staticHall = null; /** Marker for "please delete this system" */ @@ -753,10 +753,12 @@ public boolean isHiringHall(LocalDate date) { * @return The hiring hall level on the given date */ public HiringHallLevel getHiringHallLevel(LocalDate date) { + if (staticHall != null && staticHall.isActive(date)) { + return staticHall.getLevel(); + } if (getPopulation(date) == 0) { return HiringHallLevel.NONE; } - int score = 0; for (Faction faction : getFactionSet(date)) { if (faction.isPirate() || faction.isChaos()) { return HiringHallLevel.QUESTIONABLE; @@ -765,8 +767,18 @@ public HiringHallLevel getHiringHallLevel(LocalDate date) { return HiringHallLevel.NONE; } } + int score = calculateHiringHallScore(date); + return resolveHiringHallScore(score); + } + + private int calculateHiringHallScore(LocalDate date) { + int score = 0; score += getHiringHallHpgBonus(date); score += getHiringHallTechBonus(date); + return score; + } + + private HiringHallLevel resolveHiringHallScore(int score) { if (score > 9) { return HiringHallLevel.GREAT; } else if (score > 6) { From 962c1698cdfbbb784d0f9a3b0f57872182bb4707 Mon Sep 17 00:00:00 2001 From: algebro Date: Sat, 19 Oct 2024 13:48:30 -0400 Subject: [PATCH 110/118] remove redundant qualifier --- MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java b/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java index dc44ed777d..a026f5fa26 100644 --- a/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java +++ b/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java @@ -35,7 +35,7 @@ public class HiringHallOverride { private LocalDate start; @XmlJavaTypeAdapter(value = LocalDateAdapter.class) private LocalDate end; - @XmlElement(name = "level", required = false) + @XmlElement(name = "level") private HiringHallLevel level; From ef496b1b275380974733337b67fab3f858e73696 Mon Sep 17 00:00:00 2001 From: algebro Date: Mon, 21 Oct 2024 10:44:36 -0400 Subject: [PATCH 111/118] add missing / --- MekHQ/data/universe/systems.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/data/universe/systems.xml b/MekHQ/data/universe/systems.xml index 457722a701..51eec658d6 100644 --- a/MekHQ/data/universe/systems.xml +++ b/MekHQ/data/universe/systems.xml @@ -499007,7 +499007,7 @@ Rim Motors 2 3031-01-01 - 3067-10-15 + 3067-10-15 GREAT From e874757fe2dd2e2e49ab008b66c63e23d9925f6b Mon Sep 17 00:00:00 2001 From: algebro Date: Mon, 21 Oct 2024 10:54:21 -0400 Subject: [PATCH 112/118] fix handling of null dates in isActive() --- .../campaign/universe/HiringHallOverride.java | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java b/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java index a026f5fa26..3953615473 100644 --- a/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java +++ b/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java @@ -18,10 +18,7 @@ */ package mekhq.campaign.universe; -import jakarta.xml.bind.annotation.XmlAccessType; -import jakarta.xml.bind.annotation.XmlAccessorType; -import jakarta.xml.bind.annotation.XmlElement; -import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.*; import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import mekhq.adapter.LocalDateAdapter; import mekhq.campaign.universe.enums.HiringHallLevel; @@ -32,28 +29,11 @@ @XmlAccessorType(value = XmlAccessType.FIELD) public class HiringHallOverride { @XmlJavaTypeAdapter(value = LocalDateAdapter.class) - private LocalDate start; + private LocalDate start = null; @XmlJavaTypeAdapter(value = LocalDateAdapter.class) - private LocalDate end; - @XmlElement(name = "level") - private HiringHallLevel level; - - - public LocalDate getStart() { - return start; - } - - public void setStart(LocalDate start) { - this.start = start; - } - - public LocalDate getEnd() { - return end; - } - - public void setEnd(LocalDate end) { - this.end = end; - } + private LocalDate end = null; + @XmlElement + private HiringHallLevel level = HiringHallLevel.NONE; public HiringHallLevel getLevel() { return level; @@ -64,6 +44,15 @@ public void setLevel(HiringHallLevel level) { } public boolean isActive(LocalDate date) { + // Hall has no start date, so it's always inactive + if (start == null) { + return false; + } + // Hall has a start date and no end date, so it's always active + if (end == null) { + return true; + } + // Hall has a start date and end date, so it's only active between those dates return date.isAfter(start) && date.isBefore(end); } } From 44e2aa4212d8ed53f4f3ed55db184d84b37fb1c3 Mon Sep 17 00:00:00 2001 From: algebro Date: Mon, 21 Oct 2024 10:59:16 -0400 Subject: [PATCH 113/118] add docstrings --- .../campaign/universe/HiringHallOverride.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java b/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java index 3953615473..8c9dcef3b6 100644 --- a/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java +++ b/MekHQ/src/mekhq/campaign/universe/HiringHallOverride.java @@ -25,6 +25,14 @@ import java.time.LocalDate; +/** + * Class representing an "override" for the dynamic hiring hall system. Normally, hiring halls are + * generated dynamically based on planetary system factors like tech level and HPG quality, but some + * canonical systems should have hiring halls of certain qualities despite what the dynamic formula + * says. + * Overrides are stored as child elements of planetary systems in systems.xml, with a start date, + * optional end date, and quality. + */ @XmlRootElement(name = "hiringHall") @XmlAccessorType(value = XmlAccessType.FIELD) public class HiringHallOverride { @@ -35,14 +43,31 @@ public class HiringHallOverride { @XmlElement private HiringHallLevel level = HiringHallLevel.NONE; + /** + * Gets the level of the hiring hall for this override + * + * @return The hiring hall level as an enum + */ public HiringHallLevel getLevel() { return level; } + /** + * Sets the hiring hall level for this override + * + * @param level The level of hiring hall + */ public void setLevel(HiringHallLevel level) { this.level = level; } + /** + * Checks whether the hiring hall is active on a certain date. Returns true if no end date is + * specified in the override. + * + * @param date The date to check whether the hiring hall is active + * @return boolean representing whether the hiring hall is active + */ public boolean isActive(LocalDate date) { // Hall has no start date, so it's always inactive if (start == null) { From 43d1e34c603e510f4f7e0163696d7dd8971487d2 Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 21 Oct 2024 09:20:00 -0700 Subject: [PATCH 114/118] ForceRenderer - Add some ReportingUtilities --- MekHQ/src/mekhq/gui/ForceRenderer.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/MekHQ/src/mekhq/gui/ForceRenderer.java b/MekHQ/src/mekhq/gui/ForceRenderer.java index a1ddb73f6b..236d718440 100644 --- a/MekHQ/src/mekhq/gui/ForceRenderer.java +++ b/MekHQ/src/mekhq/gui/ForceRenderer.java @@ -33,6 +33,7 @@ import mekhq.campaign.force.Force; import mekhq.campaign.personnel.Person; import mekhq.campaign.unit.Unit; +import mekhq.utilities.ReportingUtilities; public class ForceRenderer extends DefaultTreeCellRenderer { private static final MMLogger logger = MMLogger.create(ForceRenderer.class); @@ -51,7 +52,8 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean setOpaque(false); if (value instanceof Unit) { - String name = "No Crew"; + String name = ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), "No Crew"); if (((Unit) value).getEntity() instanceof GunEmplacement) { name = "AutoTurret"; } @@ -64,14 +66,14 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean name += " (" + unit.getEntity().getCrew().getGunnery() + '/' + unit.getEntity().getCrew().getPiloting() + ')'; if (person.needsFixing() || (unit.getEntity().getCrew().getHits() > 0)) { - name = "" + name - + ""; + name = ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), name); } } - String uname = "" + unit.getName() + ""; + String unitName = "" + unit.getName() + ""; if (unit.isDamaged()) { - uname = "" + uname - + ""; + unitName = ReportingUtilities.messageSurroundedBySpanWithColor( + MekHQ.getMHQOptions().getFontColorNegativeHexColor(), unitName); } Entity entity = unit.getEntity(); @@ -127,7 +129,7 @@ public Component getTreeCellRendererComponent(JTree tree, Object value, boolean transport.append("
Transported by: ") .append(unit.getTransportShipAssignment().getTransportShip().getName()); } - String text = name + ", " + uname + c3network + transport; + String text = name + ", " + unitName + c3network + transport; Force force = unit.getCampaign().getForce(unit.getForceId()); if((null != person) && (null != force) && (person.getId() == force.getForceCommanderID())) { From 43aeea388af52266281f2f539f7e86590871815c Mon Sep 17 00:00:00 2001 From: Weaver Date: Mon, 21 Oct 2024 09:25:33 -0700 Subject: [PATCH 115/118] Remove some no-longer-needed cleanup --- MekHQ/src/mekhq/campaign/market/PartsStore.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/market/PartsStore.java b/MekHQ/src/mekhq/campaign/market/PartsStore.java index 9fc743f925..fc1c785f99 100644 --- a/MekHQ/src/mekhq/campaign/market/PartsStore.java +++ b/MekHQ/src/mekhq/campaign/market/PartsStore.java @@ -88,8 +88,6 @@ public void stock(Campaign c) { stockProtomekComponents(c); stockBattleArmorSuits(c); - Pattern cleanUp1 = Pattern.compile("\\d+\\shit\\(s\\),\\s"); - Pattern cleanUp2 = Pattern.compile("\\d+\\shit\\(s\\)"); StringBuilder sb = new StringBuilder(); for (Part p : parts) { p.setBrandNew(true); @@ -97,7 +95,6 @@ public void stock(Campaign c) { sb.append(p.getName()); if (!(p instanceof Armor)) { // ProtoMekArmor and BaArmor are derived from Armor String details = p.getDetails(); - details = cleanUp2.matcher(cleanUp1.matcher(details).replaceFirst("")).replaceFirst(""); if (!details.isEmpty()) { sb.append(" (").append(details).append(")"); } From e4c6f66a863965efd7886c73ead2fc8c63370d2c Mon Sep 17 00:00:00 2001 From: algebro Date: Mon, 21 Oct 2024 12:44:53 -0400 Subject: [PATCH 116/118] rename method to be less confusing --- MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java b/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java index 768594bcad..0e9a36c338 100644 --- a/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java +++ b/MekHQ/src/mekhq/campaign/universe/PlanetarySystem.java @@ -768,7 +768,7 @@ public HiringHallLevel getHiringHallLevel(LocalDate date) { } } int score = calculateHiringHallScore(date); - return resolveHiringHallScore(score); + return resolveHiringHallLevel(score); } private int calculateHiringHallScore(LocalDate date) { @@ -778,7 +778,7 @@ private int calculateHiringHallScore(LocalDate date) { return score; } - private HiringHallLevel resolveHiringHallScore(int score) { + private HiringHallLevel resolveHiringHallLevel(int score) { if (score > 9) { return HiringHallLevel.GREAT; } else if (score > 6) { From e120b82be48a9e6c415f12258ffac63137eaabde Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Mon, 21 Oct 2024 12:09:33 -0500 Subject: [PATCH 117/118] Prevented Dropout and Some Graduation Events Occurring for Very Young Children Added age check to prevent dropout and 'barely' graduation events for children under 7. --- .../personnel/education/EducationController.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java index cc4fc58969..a308ac2fb6 100644 --- a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java +++ b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java @@ -673,8 +673,11 @@ private static void processNewWeekChecks(Campaign campaign, Academy academy, Per } // does person want to drop out? - if (checkForDropout(campaign, academy, person, resources)) { - return; + // We don't process the dropout events for very young children. + if (person.getAge(campaign.getLocalDate()) > 6) { + if (checkForDropout(campaign, academy, person, resources)) { + return; + } } // was there a training accident? @@ -1360,6 +1363,11 @@ private static void graduateChild(Campaign campaign, Person person, Academy acad graduationRoll += Intelligence.parseToInt(person.getIntelligence()) - 12; } + // We don't process the granularity of graduation events for very young children. + if (person.getAge(campaign.getLocalDate()) <= 6) { + graduationRoll = 30; + } + if (graduationRoll < 30) { if (academy.isHomeSchool()) { String reportMessage = String.format(resources.getString("graduatedBarelyHomeSchooled.text"), From 39d3485c3cff00d35f2ad9b726183187c45baffc Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Mon, 21 Oct 2024 12:30:03 -0500 Subject: [PATCH 118/118] Fixed Portrait Folder Names Fixed a number of portrait folder names so that they match those in the MegaMek repo --- .../Female/{Mek Tech => MekTech}/MekTek_F_1.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_10.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_11.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_12.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_13.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_14.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_15.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_16.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_17.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_18.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_19.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_2.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_20.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_21.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_22.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_23.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_3.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_4.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_5.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_6.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_7.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_8.png | Bin .../Female/{Mek Tech => MekTech}/MekTek_F_9.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_1.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_10.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_11.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_12.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_13.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_14.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_15.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_16.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_17.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_18.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_19.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_2.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_20.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_21.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_22.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_23.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_24.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_25.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_26.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_3.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_4.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_5.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_6.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_7.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_8.png | Bin .../Male/{Mek Tech => MekTech}/MekTek_M_9.png | Bin 49 files changed, 0 insertions(+), 0 deletions(-) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_1.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_10.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_11.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_12.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_13.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_14.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_15.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_16.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_17.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_18.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_19.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_2.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_20.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_21.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_22.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_23.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_3.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_4.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_5.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_6.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_7.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_8.png (100%) rename MekHQ/data/images/portraits/Female/{Mek Tech => MekTech}/MekTek_F_9.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_1.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_10.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_11.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_12.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_13.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_14.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_15.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_16.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_17.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_18.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_19.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_2.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_20.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_21.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_22.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_23.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_24.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_25.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_26.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_3.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_4.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_5.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_6.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_7.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_8.png (100%) rename MekHQ/data/images/portraits/Male/{Mek Tech => MekTech}/MekTek_M_9.png (100%) diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_1.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_1.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_1.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_1.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_10.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_10.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_10.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_10.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_11.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_11.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_11.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_11.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_12.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_12.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_12.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_12.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_13.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_13.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_13.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_13.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_14.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_14.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_14.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_14.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_15.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_15.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_15.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_15.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_16.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_16.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_16.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_16.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_17.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_17.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_17.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_17.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_18.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_18.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_18.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_18.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_19.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_19.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_19.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_19.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_2.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_2.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_2.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_2.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_20.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_20.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_20.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_20.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_21.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_21.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_21.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_21.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_22.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_22.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_22.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_22.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_23.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_23.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_23.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_23.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_3.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_3.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_3.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_3.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_4.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_4.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_4.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_4.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_5.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_5.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_5.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_5.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_6.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_6.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_6.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_6.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_7.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_7.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_7.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_7.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_8.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_8.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_8.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_8.png diff --git a/MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_9.png b/MekHQ/data/images/portraits/Female/MekTech/MekTek_F_9.png similarity index 100% rename from MekHQ/data/images/portraits/Female/Mek Tech/MekTek_F_9.png rename to MekHQ/data/images/portraits/Female/MekTech/MekTek_F_9.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_1.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_1.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_1.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_1.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_10.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_10.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_10.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_10.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_11.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_11.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_11.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_11.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_12.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_12.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_12.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_12.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_13.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_13.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_13.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_13.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_14.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_14.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_14.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_14.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_15.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_15.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_15.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_15.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_16.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_16.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_16.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_16.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_17.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_17.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_17.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_17.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_18.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_18.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_18.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_18.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_19.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_19.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_19.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_19.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_2.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_2.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_2.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_2.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_20.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_20.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_20.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_20.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_21.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_21.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_21.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_21.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_22.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_22.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_22.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_22.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_23.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_23.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_23.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_23.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_24.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_24.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_24.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_24.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_25.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_25.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_25.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_25.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_26.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_26.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_26.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_26.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_3.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_3.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_3.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_3.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_4.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_4.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_4.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_4.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_5.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_5.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_5.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_5.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_6.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_6.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_6.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_6.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_7.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_7.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_7.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_7.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_8.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_8.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_8.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_8.png diff --git a/MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_9.png b/MekHQ/data/images/portraits/Male/MekTech/MekTek_M_9.png similarity index 100% rename from MekHQ/data/images/portraits/Male/Mek Tech/MekTek_M_9.png rename to MekHQ/data/images/portraits/Male/MekTech/MekTek_M_9.png