From 8955b03c6543de60d199ad885532c3aaf5681ec7 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 6 Apr 2025 01:49:40 -0500 Subject: [PATCH 01/10] Refactored Phenotype Enum And Added Attribute Randomization - Replaced static resource-based getters in `Phenotype` with dynamic methods using resource bundles (`getLabel`, `getTooltip`, `getShortName`). - Introduced attribute modifiers and bonus traits for `Phenotype`. - Added the `isTrueborn` method for Clan phenotype classification. - Deprecation of legacy methods like `getToolTipText`, `getName`, `parseFromString`, and `getGroupingName`. - Reimplemented parsing logic with the `fromString` method supporting legacy inputs, labels, and ordinal formats. - Implemented and applied random attribute generation in `RandomSkillPreferences` and `DefaultPersonnelGenerator`. - Added UI elements and tooltips for randomizing attributes. - Enhanced unit tests for attribute randomization and label validity. --- .../CampaignOptionsDialog.properties | 10 +- .../mekhq/resources/Personnel.properties | 21 - .../mekhq/resources/Phenotype.properties | 25 ++ .../campaign/RandomSkillPreferences.java | 29 +- .../personnel/enums/PersonnelRole.java | 69 +++- .../campaign/personnel/enums/Phenotype.java | 363 +++++++++++------ .../generator/AbstractSkillGenerator.java | 15 +- .../generator/DefaultPersonnelGenerator.java | 1 + .../generator/DefaultSkillGenerator.java | 49 ++- .../contents/AdvancementTab.java | 14 +- MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java | 369 +++++++++++------- .../personnel/enums/PhenotypeTest.java | 158 ++------ 12 files changed, 691 insertions(+), 432 deletions(-) create mode 100644 MekHQ/resources/mekhq/resources/Phenotype.properties diff --git a/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties b/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties index efaccbeb82f..60bdbe2b65f 100644 --- a/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties +++ b/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties @@ -1790,12 +1790,20 @@ lblContractNegotiationXP.tooltip=How much experience does a contract negotiator
Recommended: Keep this disabled. # skillRandomizationTab lblSkillRandomizationTab.text=Skill Randomization Options \u270E -lblExtraRandomness.text=Extra Randomness \u26A0 +lblExtraRandomness.text=Extra Random Skills \u26A0 lblExtraRandomness.tooltip=If checked, an additional 1d6 will be rolled per skill possessed by a\ \ newly created character. On a 1, the skill will be lowered, and on a 6 the skill will be raised.\
\
Warning: Due to the way experience levels are calculated, enabling this option will\ \ more frequently have characters created with slightly lower than normal experience levels. +lblRandomizeAttributes.text=Extra Random Attributes \uD83C\uDF1F +lblRandomizeAttributes.tooltip=If checked, an extra d6 is rolled for each of the character's ATOW Attributes.\ +
\ +
On a roll of a 6 the Attribute is increased by 1, and a second d6 is rolled. If that is also a 6, the Attribute is\ + \ increased by 1 again; and so on, until 6s stop being rolled.\ +
\ +
On a roll of a 6 the Attribute is decreased by 1, and a second d6 is rolled. If that is also a 1, the Attribute is\ + \ decreased by 1 again; and so on, until 1s stop being rolled. lblPhenotypesPanel.text=Clan Trueborn Percentages lblMekWarrior.text=MekWarrior lblMekWarrior.tooltip=What percentage of Clan MekWarriors should have a Trueborn phenotype? diff --git a/MekHQ/resources/mekhq/resources/Personnel.properties b/MekHQ/resources/mekhq/resources/Personnel.properties index 0549df466ad..becaffe85b5 100644 --- a/MekHQ/resources/mekhq/resources/Personnel.properties +++ b/MekHQ/resources/mekhq/resources/Personnel.properties @@ -306,27 +306,6 @@ PersonnelRole.ADMINISTRATOR_TRANSPORT.text=Admin/Transport PersonnelRole.ADMINISTRATOR_HR.text=Admin/HR PersonnelRole.DEPENDENT.text=Dependent PersonnelRole.NONE.text=None -# Phenotype Enum -Phenotype.MEKWARRIOR.text=MekWarrior -Phenotype.MEKWARRIOR.toolTipText=Probability of a trueborn person with appropriate bonuses for MekWarriors. Does not apply to non-clan factions. -Phenotype.ELEMENTAL.text=Elemental -Phenotype.ELEMENTAL.toolTipText=Probability of a trueborn person with appropriate bonuses for Elementals. Does not apply to non-clan factions. -Phenotype.AEROSPACE.text=Aerospace -Phenotype.AEROSPACE.groupingNameText=Aerospace Pilot -Phenotype.AEROSPACE.toolTipText=Probability of a trueborn person with appropriate bonuses for Aerospace crews. Does not apply to non-clan factions. -Phenotype.VEHICLE.text=Vehicle -Phenotype.VEHICLE.groupingNameText=Vehicle Crew -Phenotype.VEHICLE.toolTipText=Probability of a trueborn person with appropriate bonuses for Vehicle crews. This only applies to Clan Hell's Horses. -Phenotype.PROTOMEK.text=ProtoMek -Phenotype.PROTOMEK.groupingNameText=ProtoMek Pilot -Phenotype.PROTOMEK.toolTipText=Probability of a trueborn person with appropriate bonuses for ProtoMek crews. Does not apply to non-clan factions, nor clans before 3060. -Phenotype.NAVAL.text=Naval -Phenotype.NAVAL.groupingNameText=Naval Commander -Phenotype.NAVAL.toolTipText=Probability of a trueborn person with appropriate bonuses for Naval crews. This only applies to Clan Snow Raven and the Outworlds Alliance. -Phenotype.NONE.text=None -Phenotype.NONE.toolTipText=This person is freeborn -Phenotype.GENERAL.text=General -Phenotype.GENERAL.toolTipText=Error: this should never be displayed and is used for generation # Profession Enum Profession.MEKWARRIOR.text=MekWarrior Profession.MEKWARRIOR.toolTipText=The MekWarrior Profession contains MekWarriors, LAM Pilots, and ProtoMek Pilots. diff --git a/MekHQ/resources/mekhq/resources/Phenotype.properties b/MekHQ/resources/mekhq/resources/Phenotype.properties new file mode 100644 index 00000000000..c81f60d4792 --- /dev/null +++ b/MekHQ/resources/mekhq/resources/Phenotype.properties @@ -0,0 +1,25 @@ +# suppress inspection "UnusedProperty" for whole file +## General +shortName.trueborn=Trueborn +shortName.freeborn=Freeborn +## Specific +MEKWARRIOR.label=MekWarrior +MEKWARRIOR.tooltip=Probability of a trueborn person with appropriate bonuses for MekWarriors. Does not apply to non-clan factions. +ELEMENTAL.label=Elemental +ELEMENTAL.tooltip=Probability of a trueborn person with appropriate bonuses for Elementals. Does not apply to non-clan factions. +AEROSPACE.label=Aerospace +AEROSPACE.groupingNameText=Aerospace Pilot +AEROSPACE.tooltip=Probability of a trueborn person with appropriate bonuses for Aerospace crews. Does not apply to non-clan factions. +VEHICLE.label=Vehicle +VEHICLE.groupingNameText=Vehicle Crew +VEHICLE.tooltip=Probability of a trueborn person with appropriate bonuses for Vehicle crews. This only applies to Clan Hell's Horses. +PROTOMEK.label=ProtoMek +PROTOMEK.groupingNameText=ProtoMek Pilot +PROTOMEK.tooltip=Probability of a trueborn person with appropriate bonuses for ProtoMek crews. Does not apply to non-clan factions, nor clans before 3060. +NAVAL.label=Naval +NAVAL.groupingNameText=Naval Commander +NAVAL.tooltip=Probability of a trueborn person with appropriate bonuses for Naval crews. This only applies to Clan Snow Raven and the Outworlds Alliance. +NONE.label=None +NONE.tooltip=This person is freeborn +GENERAL.label=General +GENERAL.tooltip=Error: this should never be displayed and is used for generation diff --git a/MekHQ/src/mekhq/campaign/RandomSkillPreferences.java b/MekHQ/src/mekhq/campaign/RandomSkillPreferences.java index 58f7c6a3b96..4103eaf8599 100644 --- a/MekHQ/src/mekhq/campaign/RandomSkillPreferences.java +++ b/MekHQ/src/mekhq/campaign/RandomSkillPreferences.java @@ -52,6 +52,7 @@ public class RandomSkillPreferences { private int overallRecruitBonus; Map recruitmentBonuses; private boolean randomizeSkill; + private boolean randomizeAttributes; private boolean useClanBonuses; private int antiMekProb; private int[] specialAbilityBonus; @@ -68,7 +69,7 @@ public RandomSkillPreferences() { overallRecruitBonus = 0; recruitmentBonuses = new HashMap<>(); randomizeSkill = true; - useClanBonuses = true; + randomizeAttributes = false; antiMekProb = 10; combatSmallArmsBonus = -3; supportSmallArmsBonus = -10; @@ -165,18 +166,34 @@ public void setSpecialAbilityBonus(int type, int bonus) { } } + public boolean randomizeSkill() { + return randomizeSkill; + } + public void setRandomizeSkill(boolean b) { this.randomizeSkill = b; } - public boolean randomizeSkill() { - return randomizeSkill; + public boolean isRandomizeAttributes() { + return randomizeAttributes; + } + + public void setRandomizeAttributes(boolean isRandomizeAttributes) { + this.randomizeAttributes = isRandomizeAttributes; } + /** + * @deprecated not in use. + */ + @Deprecated(since = "0.50.05", forRemoval = true) public void setUseClanBonuses(boolean b) { this.useClanBonuses = b; } + /** + * @deprecated not in use. + */ + @Deprecated(since = "0.50.05", forRemoval = true) public boolean useClanBonuses() { return useClanBonuses; } @@ -269,7 +286,7 @@ public void writeToXML(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "commandSkillsModifier", commandSkillsModifier); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "roleplaySkillsModifier", roleplaySkillsModifier); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomizeSkill", randomizeSkill); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useClanBonuses", useClanBonuses); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomizeAttributes", randomizeAttributes); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "antiMekProb", antiMekProb); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "combatSmallArmsBonus", combatSmallArmsBonus); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "supportSmallArmsBonus", supportSmallArmsBonus); @@ -303,8 +320,8 @@ public static RandomSkillPreferences generateRandomSkillPreferencesFromXml(Node retVal.overallRecruitBonus = Integer.parseInt(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("randomizeSkill")) { retVal.randomizeSkill = wn2.getTextContent().equalsIgnoreCase("true"); - } else if (wn2.getNodeName().equalsIgnoreCase("useClanBonuses")) { - retVal.useClanBonuses = wn2.getTextContent().equalsIgnoreCase("true"); + } else if (wn2.getNodeName().equalsIgnoreCase("randomizeAttributes")) { + retVal.randomizeAttributes = wn2.getTextContent().equalsIgnoreCase("true"); } else if (wn2.getNodeName().equalsIgnoreCase("antiMekProb")) { retVal.antiMekProb = Integer.parseInt(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("combatSmallArmsBonus")) { diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java b/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java index 88f294b04e5..d2e80c9e348 100644 --- a/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java +++ b/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java @@ -37,6 +37,7 @@ import megamek.common.annotations.Nullable; import megamek.logging.MMLogger; import mekhq.MekHQ; +import mekhq.campaign.personnel.skills.enums.SkillAttribute; /** * The PersonnelRole enum represents various roles a person can have. Each role is associated with a name, an optional @@ -57,7 +58,16 @@ public enum PersonnelRole { AEROSPACE_PILOT("PersonnelRole.AEROSPACE_PILOT.text", KeyEvent.VK_A), CONVENTIONAL_AIRCRAFT_PILOT("PersonnelRole.CONVENTIONAL_AIRCRAFT_PILOT.text", KeyEvent.VK_C), PROTOMEK_PILOT("PersonnelRole.PROTOMEK_PILOT.text", KeyEvent.VK_P), - BATTLE_ARMOUR("PersonnelRole.BATTLE_ARMOUR.text", "PersonnelRole.BATTLE_ARMOUR.clan.text", KeyEvent.VK_B), + BATTLE_ARMOUR("PersonnelRole.BATTLE_ARMOUR.text", + "PersonnelRole.BATTLE_ARMOUR.clan.text", + KeyEvent.VK_B, + 0, + 0, + 0, + 0, + 0, + 0, + 0), SOLDIER("PersonnelRole.SOLDIER.text", KeyEvent.VK_S), VESSEL_PILOT("PersonnelRole.VESSEL_PILOT.text", KeyEvent.VK_I), VESSEL_GUNNER("PersonnelRole.VESSEL_GUNNER.text", KeyEvent.VK_U), @@ -82,6 +92,13 @@ public enum PersonnelRole { private final String name; private final String clanName; private final int mnemonic; // Unused: J, K, Q, X, Z + private final int strength; + private final int body; + private final int dexterity; + private final int reflexes; + private final int intelligence; + private final int willpower; + private final int charisma; // endregion Variable Declarations // region Constructors @@ -93,7 +110,7 @@ public enum PersonnelRole { * @param mnemonic the mnemonic of the personnel role */ PersonnelRole(final String name, final int mnemonic) { - this(name, null, mnemonic); + this(name, null, mnemonic, 0, 0, 0, 0, 0, 0, 0); } /** @@ -104,12 +121,21 @@ public enum PersonnelRole { * @param clanName the clan name of the role can be {@code null}. * @param mnemonic the mnemonic associated with the role. */ - PersonnelRole(final String name, @Nullable final String clanName, final int mnemonic) { + PersonnelRole(final String name, @Nullable final String clanName, final int mnemonic, final int strength, + final int body, final int reflexes, final int dexterity, final int intelligence, final int willpower, + final int charisma) { final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel", MekHQ.getMHQOptions().getLocale()); this.name = resources.getString(name); this.clanName = (clanName == null) ? this.name : resources.getString(clanName); this.mnemonic = mnemonic; + this.strength = strength; + this.body = body; + this.reflexes = reflexes; + this.dexterity = dexterity; + this.intelligence = intelligence; + this.willpower = willpower; + this.charisma = charisma; } // endregion Constructors @@ -121,6 +147,43 @@ public String getName(final boolean isClan) { public int getMnemonic() { return mnemonic; } + + /** + * Retrieves the corresponding modifier value for the given {@link SkillAttribute}. + * + *

This method determines the modifier by matching the input {@link SkillAttribute} + * to its associated property within the class. The mapping is as follows:

+ * + * + * @param attribute The {@link SkillAttribute} for which the modifier value is requested. Must not be {@code null}. + * + * @return The integer value of the modifier corresponding to the given {@link SkillAttribute}. + */ + public int getAttributeModifier(final SkillAttribute attribute) { + if (attribute == null) { + return 0; + } + + return switch (attribute) { + case NONE -> 0; + case STRENGTH -> strength; + case BODY -> body; + case REFLEXES -> reflexes; + case DEXTERITY -> dexterity; + case INTELLIGENCE -> intelligence; + case WILLPOWER -> willpower; + case CHARISMA -> charisma; + }; + } // endregion Getters // region Boolean Comparison Methods diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java b/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java index aee5955fe34..9439f19b693 100644 --- a/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java +++ b/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java @@ -27,126 +27,111 @@ */ package mekhq.campaign.personnel.enums; -import megamek.logging.MMLogger; -import mekhq.MekHQ; +import static mekhq.utilities.MHQInternationalization.getFormattedTextAt; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; -import java.util.ResourceBundle; -import java.util.stream.Collectors; + +import megamek.codeUtilities.MathUtility; +import megamek.logging.MMLogger; +import mekhq.campaign.personnel.skills.enums.SkillAttribute; /** - * The {@link Phenotype} Enum represents various phenotypes a Clan character can have. - * Each {@link Phenotype} is associated with a name, short name, grouping name and a tooltip text. - * Each {@link Phenotype} can be classified as either {@code external} or {@code internal}. + * The {@link Phenotype} Enum represents various phenotypes a Clan character can have. Each {@link Phenotype} is + * associated with a name, short name, grouping name and a tooltip text. Each {@link Phenotype} can be classified as + * either {@code external} or {@code internal}. */ public enum Phenotype { // region Enum Declarations /** * Individual external phenotypes. */ - MEKWARRIOR("Phenotype.MEKWARRIOR.text", "Trueborn.text", - "Phenotype.MEKWARRIOR.text", "Phenotype.MEKWARRIOR.toolTipText"), - ELEMENTAL("Phenotype.ELEMENTAL.text", "Trueborn.text", - "Phenotype.ELEMENTAL.text", "Phenotype.ELEMENTAL.toolTipText"), - AEROSPACE("Phenotype.AEROSPACE.text", "Trueborn.text", - "Phenotype.AEROSPACE.groupingNameText", "Phenotype.AEROSPACE.toolTipText"), - VEHICLE("Phenotype.VEHICLE.text", "Trueborn.text", - "Phenotype.VEHICLE.groupingNameText", "Phenotype.VEHICLE.toolTipText"), - PROTOMEK("Phenotype.PROTOMEK.text", "Trueborn.text", - "Phenotype.PROTOMEK.groupingNameText", "Phenotype.PROTOMEK.toolTipText"), - NAVAL("Phenotype.NAVAL.text", "Trueborn.text", - "Phenotype.NAVAL.groupingNameText", "Phenotype.NAVAL.toolTipText"), + MEKWARRIOR(true, true, 0, 0, 1, 1, new ArrayList<>()), + ELEMENTAL(true, true, 2, 1, -1, 0, List.of("atow_toughness")), + AEROSPACE(true, true, -1, -1, +2, +2, List.of("flaw_glass_jaw")), + // ATOW doesn't cover a vehicle phenotype, but as the linked attributes for vehicle skills are also reflexes and + // dexterity I copied the MekWarrior phenotype + VEHICLE(true, true, 0, 0, 1, 1, new ArrayList<>()), + // According to my research, ProtoMek pilots are normally just Aerospace washouts, so I'm assuming they'd have the + // same phenotype modifiers. + PROTOMEK(true, true, -1, -1, +2, +2, List.of("flaw_glass_jaw")), + // Copying the MekWarrior phenotype, same reasons as above. + NAVAL(true, true, 0, 0, 1, 1, new ArrayList<>()), /** * Individual internal phenotypes. */ // Internal Phenotypes - NONE("Phenotype.NONE.text", "Freeborn.text", - "Phenotype.NONE.text", "Phenotype.NONE.toolTipText", false), - GENERAL("Phenotype.GENERAL.text", "Trueborn.text", - "Phenotype.GENERAL.text", "Phenotype.GENERAL.toolTipText", false); + NONE(false, false, 0, 0, 0, 0, new ArrayList<>()), + GENERAL(false, false, 0, 0, 0, 0, new ArrayList<>()); // endregion Enum Declarations // region Variable Declarations - private final String name; - private final String shortName; - private final String groupingName; - private final String toolTipText; + private static final MMLogger logger = MMLogger.create(Phenotype.class); + private static final String RESOURCE_BUNDLE = "mekhq.resources." + Phenotype.class.getSimpleName(); + + private final boolean isTrueborn; private final boolean external; + private final int strength; + private final int body; + private final int reflexes; + private final int dexterity; + private final List bonusTraits; // endregion Variable Declarations // region Constructors - /** - * Overloaded constructor to create an external {@link Phenotype}. - * - * @param name the name of the phenotype. - * @param shortName the short name for phenotype. - * @param groupingName the group the phenotype belongs to. - * @param toolTipText tooltip text for the phenotype. - */ - Phenotype(final String name, final String shortName, final String groupingName, - final String toolTipText) { - this(name, shortName, groupingName, toolTipText, true); + Phenotype() { + this(false, false, 0, 0, 0, 0, new ArrayList<>()); } - /** - * Overloaded constructor to create a {@link Phenotype}, either external or internal. - * - * @param name the name of the phenotype. - * @param shortName the short name for phenotype. - * @param groupingName the group the phenotype belongs to. - * @param toolTipText tooltip text for the phenotype. - * @param external a boolean denoting whether the phenotype is internal ({@code false}) or - * external ({@code true}). - */ - Phenotype(final String name, final String shortName, final String groupingName, - final String toolTipText, final boolean external) { - final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel", - MekHQ.getMHQOptions().getLocale()); - this.name = resources.getString(name); - this.shortName = resources.getString(shortName); - this.groupingName = resources.getString(groupingName); - this.toolTipText = resources.getString(toolTipText); + Phenotype(final boolean isTrueborn, final boolean external, final int strength, final int body, final int reflexes, + final int dexterity, final List bonusTraits) { + this.isTrueborn = isTrueborn; this.external = external; + this.strength = strength; + this.body = body; + this.reflexes = reflexes; + this.dexterity = dexterity; + this.bonusTraits = bonusTraits; } // endregion Constructors // region Getters + /** - * Retrieves the name of the phenotype. - * - * @return a {@link String} representing the name of the phenotype. + * @deprecated use {@link #getLabel()} instead */ + @Deprecated(since = "0.50.05", forRemoval = true) public String getName() { - return name; + return getLabel(); } /** - * Retrieves the short name of the phenotype. - * - * @return a {@link String} representing the short name of the phenotype. + * @deprecated use {@link #getLabel()} instead */ - public String getShortName() { - return shortName; + @Deprecated(since = "0.50.05", forRemoval = true) + public String getGroupingName() { + return getLabel(); } /** - * Retrieves the grouping name of the phenotype. - * - * @return a {@link String} representing the grouping name of the phenotype. + * @deprecated use {@link #getTooltip()} instead */ - public String getGroupingName() { - return groupingName; + @Deprecated(since = "0.50.05", forRemoval = true) + public String getToolTipText() { + return getTooltip(); } /** - * Retrieves the tooltip text associated with the phenotype. + * Checks whether the phenotype is a Clan Trueborn phenotype. * - * @return a {@link String} representing the tooltip text of the phenotype. + * @return a boolean, {@code true} if the phenotype is Trueborn, otherwise {@code false}. + * + * @author Illiani + * @since 0.50.05 */ - public String getToolTipText() { - return toolTipText; + public boolean isTrueborn() { + return external; } /** @@ -157,9 +142,101 @@ public String getToolTipText() { public boolean isExternal() { return external; } + + /** + * Retrieves the modifier value for a given skill attribute. + * + *

The method determines the corresponding modifier based on the + * specified {@link SkillAttribute}. The value is fetched from the associated field depending on the attribute:

+ *
    + *
  • For {@code STRENGTH}, the {@code strength} field is returned.
  • + *
  • For {@code BODY}, the {@code body} field is returned.
  • + *
  • For {@code REFLEXES}, the {@code reflexes} field is returned.
  • + *
  • For {@code DEXTERITY}, the {@code dexterity} field is returned.
  • + *
  • For {@code NONE}, {@code INTELLIGENCE}, {@code WILLPOWER}, and + * {@code CHARISMA}, a default value of {@code 0} is returned.
  • + *
+ * + * @param attribute The skill attribute for which the modifier is requested. + * + * @return The modifier value associated with the provided attribute. If the attribute is {@code NONE}, + * {@code INTELLIGENCE}, {@code WILLPOWER}, or {@code CHARISMA}, the method returns {@code 0}. + * + * @throws NullPointerException If the {@code attribute} is {@code null}. + */ + public int getAttributeModifier(final SkillAttribute attribute) { + return switch (attribute) { + case STRENGTH -> strength; + case BODY -> body; + case REFLEXES -> reflexes; + case DEXTERITY -> dexterity; + case NONE, INTELLIGENCE, WILLPOWER, CHARISMA -> 0; + }; + } + + /** + * Retrieves a list of bonus traits assigned to this phenotype. + * + * @return A list of bonus traits as strings. + */ + public List getBonusTraits() { + return bonusTraits; + } + + /** + * Retrieves the short name of this phenotype based on its Clan status. + * + *

The method determines the appropriate key by appending the Clan status + * ("trueborn" or "freeborn") to the base key "shortName.". This key is then used to fetch the formatted text from + * the resource bundle.

+ * + * @return A formatted short name string corresponding to the born type (e.g., "shortName.trueborn" or + * "shortName.freeborn") from the resource bundle. + * + * @author Illiani + * @since 0.50.05 + */ + public String getShortName() { + String key = "shortName." + (isTrueborn ? "trueborn" : "freeborn"); + + return getFormattedTextAt(RESOURCE_BUNDLE, key); + } + + /** + * Retrieves the label for this phenotype. + * + *

The method constructs the key by appending ".label" to the name of + * the current instance (as returned by {@code name()}) and uses it to fetch the formatted text from the resource + * bundle.

+ * + * @return A formatted label string corresponding to the key ".label" from the resource bundle. + * + * @author Illiani + * @since 0.50.05 + */ + public String getLabel() { + return getFormattedTextAt(RESOURCE_BUNDLE, name() + ".label"); + } + + /** + * Retrieves the tooltip text for this phenotype. + * + *

The method constructs the key by appending ".tooltip" to the name of + * the current instance (as returned by {@code name()}) and uses it to fetch the formatted text from the resource + * bundle.

+ * + * @return A formatted tooltip string corresponding to the key ".tooltip" from the resource bundle. + * + * @author Illiani + * @since 0.50.05 + */ + public String getTooltip() { + return getFormattedTextAt(RESOURCE_BUNDLE, name() + ".tooltip"); + } // endregion Getters // region Boolean Comparison Methods + /** * Checks if the phenotype is MekWarrior. * @@ -236,70 +313,122 @@ public boolean isGeneral() { /** * Retrieves a list of external phenotypes. * - * @return a {@link List} of {@link Phenotype} objects where {@code isExternal()} returns - * {@code true}. + * @return a {@link List} of {@link Phenotype} objects where {@code isExternal()} returns {@code true}. */ public static List getExternalPhenotypes() { - return Arrays.stream(values()) - .filter(Phenotype::isExternal) - .collect(Collectors.toList()); + List externalPhenotypes = new ArrayList<>(); + + for (Phenotype phenotype : values()) { + if (phenotype.isExternal()) { + externalPhenotypes.add(phenotype); + } + } + + return externalPhenotypes; } + // region File I/O + /** - * Parses a string representation of a {@link Phenotype} and returns the corresponding phenotype - * object. - * If the string cannot be parsed into a valid phenotype, returns {@code Phenotype.NONE}. - * - * @param phenotype the string representation of the phenotype to parse - * @return the parsed phenotype + * @deprecated use {@link #fromString(String)} instead. */ - // region File I/O + @Deprecated(since = "0.50.05", forRemoval = true) public static Phenotype parseFromString(final String phenotype) { + return fromString(phenotype); + } + + /** + * Converts a string representation to a corresponding {@link Phenotype} value. + * + *

This method attempts to parse the input string and return the appropriate + * {@link Phenotype} instance using the following approaches:

+ *
    + *
  1. By name: Converts the input to uppercase, replaces spaces with underscores, + * and tries to match it using {@link Phenotype#valueOf(String)}.
  2. + *
  3. By label (short name): Matches the input case-insensitively against + * the short name of each {@link Phenotype}.
  4. + *
  5. Legacy compatibility: Matches certain input strings such as + * {@code "MECHWARRIOR"} and {@code "PROTOMECH"} to their updated {@link Phenotype} equivalents.
  6. + *
  7. By ordinal: Parses the input as an integer and retrieves the + * phenotype corresponding to the specified ordinal.
  8. + *
+ * + *

If the input is {@code null} or none of the parsing approaches are successful, + * an error is logged, and the method defaults to returning {@link Phenotype#NONE}.

+ * + * @param text The string representation to parse. Supported input formats include: + *
    + *
  • The full name of the phenotype (case-insensitive, spaces allowed).
  • + *
  • The short name (label) of the phenotype (case-insensitive).
  • + *
  • Legacy names such as {@code "MECHWARRIOR"} or {@code "PROTOMECH"}.
  • + *
  • An ordinal value corresponding to the phenotype.
  • + *
+ * + * @return The corresponding {@link Phenotype} instance for the given input, or {@link Phenotype#NONE} if the input + * he input is invalid or {@code null}. + * + * @author Illiani + * @since 0.50.05 + */ + public static Phenotype fromString(String text) { + if (text == null || text.isBlank()) { + logger.error("Unable to parse text into a Phenotype. Returning NONE"); + return NONE; + } + + // Parse from name try { - return valueOf(phenotype); - } catch (Exception ignored) {} + return Phenotype.valueOf(text.toUpperCase().replace(" ", "_")); + } catch (Exception ignored) { + } + // Parse from label try { - switch (Integer.parseInt(phenotype)) { - case 0: - return NONE; - case 1: - return MEKWARRIOR; - case 2: - return ELEMENTAL; - case 3: - return AEROSPACE; - case 4: - return VEHICLE; - default: - break; + for (Phenotype phenotype : Phenotype.values()) { + if (phenotype.getLabel().equalsIgnoreCase(text)) { + return phenotype; + } } - } catch (Exception ignored) {} + } catch (Exception ignored) { + } // <50.1 compatibility - switch (phenotype) { - case "MECHWARRIOR" -> { - return MEKWARRIOR; - } - case "PROTOMECH" -> { - return PROTOMEK; - } - default -> {} + if (text.equalsIgnoreCase("MECHWARRIOR")) { + return MEKWARRIOR; + } else if (text.equalsIgnoreCase("PROTOMECH")) { + return PROTOMEK; + } + + // Parse from ordinal + try { + return Phenotype.values()[MathUtility.parseInt(text, NONE.ordinal())]; + } catch (Exception ignored) { } - MMLogger.create(Phenotype.class).error( - String.format("Unable to parse %s into a Phenotype. Returning NONE.", phenotype)); + logger.error("Unable to parse {} into a Phenotype. Returning NONE", text); return NONE; } // endregion File I/O /** - * @return The string representation of a {@link Phenotype}. - * If the phenotype is {@code None} or {@code General}, it returns short name. - * Otherwise, it returns the short name followed by a space and group name. + * Returns a string representation of this phenotype. + * + *

The string is composed of the short name and, if the component is + * {@code trueborn}, the label is appended with a space separator. If the component is not {@code trueborn}, only + * the short name is included. + *

+ * + * @return A string representation consisting of the short name and, if applicable, the label, formatted as: + *
    + *
  • {@code "
  • + *
  • {@code ""} if {@code trueborn} is {@code false}
  • + *
+ * + * @author Illiani + * @since 0.50.05 */ @Override public String toString() { - return (isNone() || isGeneral()) ? getShortName() : getShortName() + ' ' + getGroupingName(); + return getShortName() + (isTrueborn ? ' ' + getLabel() : ""); } } diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSkillGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSkillGenerator.java index 36e822de292..795623e588c 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSkillGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/AbstractSkillGenerator.java @@ -35,8 +35,6 @@ import mekhq.campaign.Campaign; import mekhq.campaign.RandomSkillPreferences; import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.skills.Skill; -import mekhq.campaign.personnel.skills.SkillType; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.skills.Skill; import mekhq.campaign.personnel.skills.SkillType; @@ -78,6 +76,13 @@ public void setSkillPreferences(RandomSkillPreferences skillPreferences) { */ public abstract void generateSkills(Campaign campaign, Person person, int expLvl); + /** + * Generates attributes for a specified person based on their phenotype. + * + * @param person The {@link Person} for whom attributes are to be generated. + */ + public abstract void generateAttributes(Person person); + /** * Generates the default skills for a {@link Person} based on their primary role. * @@ -88,7 +93,7 @@ public void setSkillPreferences(RandomSkillPreferences skillPreferences) { * @param rollModifier A roll modifier to apply to any randomizations. */ protected void generateDefaultSkills(Person person, PersonnelRole primaryRole, int expLvl, int bonus, - int rollModifier) { + int rollModifier) { switch (primaryRole) { case MEKWARRIOR: addSkill(person, SkillType.S_PILOT_MEK, expLvl, rskillPrefs.randomizeSkill(), bonus, rollModifier); @@ -209,12 +214,12 @@ public static void addSkill(Person person, String skillName, int level, int bonu } protected static void addSkill(Person person, String skillName, int experienceLevel, boolean randomizeLevel, - int bonus) { + int bonus) { addSkill(person, skillName, experienceLevel, randomizeLevel, bonus, 0); } protected static void addSkill(Person person, String skillName, int experienceLevel, boolean randomizeLevel, - int bonus, int rollMod) { + int bonus, int rollMod) { if (randomizeLevel) { person.addSkill(skillName, Skill.randomizeLevel(skillName, experienceLevel, bonus, rollMod)); } else { diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java index 407a38ed3cb..8a2b9609d1d 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java @@ -103,6 +103,7 @@ public Person generate(Campaign campaign, PersonnelRole primaryRole, PersonnelRo AbstractSkillGenerator skillGenerator = new DefaultSkillGenerator(getSkillPreferences()); skillGenerator.generateSkills(campaign, person, expLvl); + skillGenerator.generateAttributes(person); // Limit skills by age for children and adolescents int age = person.getAge(campaign.getLocalDate()); diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java index 04fdc07443e..d56464e4e55 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java @@ -27,6 +27,9 @@ */ package mekhq.campaign.personnel.generator; +import static megamek.common.Compute.d6; +import static mekhq.campaign.personnel.skills.Attributes.MAXIMUM_ATTRIBUTE_SCORE; +import static mekhq.campaign.personnel.skills.Attributes.MINIMUM_ATTRIBUTE_SCORE; import static mekhq.campaign.personnel.skills.SkillDeprecationTool.DEPRECATED_SKILLS; import static mekhq.campaign.personnel.skills.enums.SkillSubType.SUPPORT_COMMAND; @@ -39,9 +42,10 @@ import mekhq.campaign.CampaignOptions; import mekhq.campaign.RandomSkillPreferences; import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.skills.SkillType; import mekhq.campaign.personnel.enums.PersonnelRole; +import mekhq.campaign.personnel.enums.Phenotype; import mekhq.campaign.personnel.skills.SkillType; +import mekhq.campaign.personnel.skills.enums.SkillAttribute; public class DefaultSkillGenerator extends AbstractSkillGenerator { //region Constructors @@ -152,4 +156,47 @@ public void generateSkills(final Campaign campaign, final Person person, final i addSkill(person, selSkill, secondLvl, rskillPrefs.randomizeSkill(), 0); } } + + @Override + public void generateAttributes(Person person) { + RandomSkillPreferences skillPreferences = getSkillPreferences(); + boolean extraRandomAttributes = skillPreferences.isRandomizeAttributes(); + + PersonnelRole profession = person.getPrimaryRole(); + Phenotype phenotype = person.getPhenotype(); + for (SkillAttribute attribute : SkillAttribute.values()) { + if (attribute.isNone()) { + continue; + } + + // Profession && Phenotype adjustments + int attributeModifier = profession.getAttributeModifier(attribute); + attributeModifier += phenotype.getAttributeModifier(attribute); + person.changeAttributeScore(attribute, attributeModifier); + + // Basic Attribute randomization + int roll = d6(); + + if (roll == 1) { + person.changeAttributeScore(attribute, -1); + } else if (roll == 6) { + person.changeAttributeScore(attribute, 1); + } + + // Extra Attribute randomness + if (extraRandomAttributes) { + roll = d6(); + + if (roll == 1) { + do { + person.changeAttributeScore(attribute, -1); + } while ((d6() == 1) && (person.getAttributeScore(attribute) > MINIMUM_ATTRIBUTE_SCORE)); + } else if (roll == 6) { + do { + person.changeAttributeScore(attribute, 1); + } while ((d6() == 1) && (person.getAttributeScore(attribute) < MAXIMUM_ATTRIBUTE_SCORE)); + } + } + } + } } diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java index 53492cfed37..78e5869069e 100644 --- a/MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java @@ -43,7 +43,6 @@ import mekhq.campaign.Campaign; import mekhq.campaign.CampaignOptions; import mekhq.campaign.RandomSkillPreferences; -import mekhq.campaign.personnel.skills.SkillType; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.enums.Phenotype; import mekhq.campaign.personnel.skills.SkillType; @@ -114,6 +113,7 @@ public class AdvancementTab { //start Skill Randomization Tab private JCheckBox chkExtraRandomness; + private JCheckBox chkRandomizeAttributes; private JPanel pnlPhenotype; private JLabel[] phenotypeLabels; @@ -520,6 +520,7 @@ private JPanel createAdministratorsPanel() { */ private void initializeSkillRandomizationTab() { chkExtraRandomness = new JCheckBox(); + chkRandomizeAttributes = new JCheckBox(); pnlPhenotype = new JPanel(); phenotypeLabels = new JLabel[] {}; // This will be initialized properly later @@ -604,6 +605,7 @@ public JPanel skillRandomizationTab() { // Contents chkExtraRandomness = new CampaignOptionsCheckBox("ExtraRandomness"); + chkRandomizeAttributes = new CampaignOptionsCheckBox("RandomizeAttributes"); pnlPhenotype = createPhenotypePanel(); pnlRandomAbilities = createAbilityPanel(); @@ -621,6 +623,10 @@ public JPanel skillRandomizationTab() { layout.gridwidth = 1; panel.add(chkExtraRandomness, layout); + layout.gridy++; + layout.gridwidth = 1; + panel.add(chkRandomizeAttributes, layout); + layout.gridx = 0; layout.gridy++; panel.add(pnlPhenotype, layout); @@ -1100,7 +1106,7 @@ public void loadValuesFromCampaignOptions() { * {@code null}, values are loaded from the current skill preferences. */ public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampaignOptions, - @Nullable RandomSkillPreferences presetRandomSkillPreferences) { + @Nullable RandomSkillPreferences presetRandomSkillPreferences) { CampaignOptions options = presetCampaignOptions; if (presetCampaignOptions == null) { options = this.campaignOptions; @@ -1132,6 +1138,7 @@ public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampai //start Skill Randomization Tab chkExtraRandomness.setSelected(skillPreferences.randomizeSkill()); + chkRandomizeAttributes.setSelected(skillPreferences.isRandomizeAttributes()); final int[] phenotypeProbabilities = options.getPhenotypeProbabilities(); for (int i = 0; i < phenotypeSpinners.length; i++) { phenotypeSpinners[i].setValue(phenotypeProbabilities[i]); @@ -1184,7 +1191,7 @@ public void loadValuesFromCampaignOptions(@Nullable CampaignOptions presetCampai * {@code null}, values are applied to the current skill preferences. */ public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampaignOptions, - @Nullable RandomSkillPreferences presetRandomSkillPreferences) { + @Nullable RandomSkillPreferences presetRandomSkillPreferences) { CampaignOptions options = presetCampaignOptions; if (presetCampaignOptions == null) { options = this.campaignOptions; @@ -1216,6 +1223,7 @@ public void applyCampaignOptionsToCampaign(@Nullable CampaignOptions presetCampa //start Skill Randomization Tab skillPreferences.setRandomizeSkill(chkExtraRandomness.isSelected()); + skillPreferences.setRandomizeAttributes(chkRandomizeAttributes.isSelected()); for (int i = 0; i < phenotypeSpinners.length; i++) { options.setPhenotypeProbability(i, (int) phenotypeSpinners[i].getValue()); } diff --git a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java index b50257bc007..9633e122b32 100644 --- a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java @@ -27,14 +27,42 @@ */ package mekhq.gui.dialog; +import static mekhq.campaign.personnel.backgrounds.BackgroundsController.randomMercenaryCompanyNameGenerator; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Cursor; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.List; +import java.util.Objects; +import java.util.StringJoiner; +import java.util.function.Predicate; +import javax.swing.*; +import javax.swing.GroupLayout.Alignment; + import megamek.client.generator.RandomCallsignGenerator; import megamek.client.generator.RandomNameGenerator; import megamek.client.ui.baseComponents.MMButton; import megamek.client.ui.baseComponents.MMComboBox; import megamek.client.ui.dialogs.EntityReadoutDialog; -import megamek.client.ui.preferences.*; +import megamek.client.ui.preferences.JComboBoxPreference; +import megamek.client.ui.preferences.JIntNumberSpinnerPreference; +import megamek.client.ui.preferences.JTabbedPanePreference; +import megamek.client.ui.preferences.JTextFieldPreference; +import megamek.client.ui.preferences.PreferencesNode; import megamek.codeUtilities.StringUtility; -import megamek.common.*; +import megamek.common.Compute; +import megamek.common.Entity; +import megamek.common.EntityWeightClass; +import megamek.common.MekFileParser; +import megamek.common.MekSummary; +import megamek.common.Messages; +import megamek.common.UnitType; import megamek.common.annotations.Nullable; import megamek.common.enums.Gender; import megamek.logging.MMLogger; @@ -58,19 +86,6 @@ import mekhq.gui.panels.LayeredForceIconCreationPanel; import mekhq.gui.utilities.JScrollPaneWithSpeed; -import javax.swing.*; -import javax.swing.GroupLayout.Alignment; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.List; -import java.util.Objects; -import java.util.StringJoiner; -import java.util.function.Predicate; - -import static mekhq.campaign.personnel.backgrounds.BackgroundsController.randomMercenaryCompanyNameGenerator; - public class GMToolsDialog extends AbstractMHQDialogBasic { private static final MMLogger logger = MMLogger.create(GMToolsDialog.class); @@ -151,7 +166,7 @@ public class GMToolsDialog extends AbstractMHQDialogBasic { private static final String[] QUALITY_NAMES = { "F", "D", "C", "B", "A", "A*" }; private static final String[] WEIGHT_NAMES = { "Light", "Medium", "Heavy", "Assault" }; private static final Integer[] BLOODNAME_ERAS = { 2807, 2825, 2850, 2900, 2950, 3000, 3050, 3060, 3075, 3085, - 3100 }; + 3100 }; // endregion Constants // endregion Variable Declarations @@ -556,15 +571,11 @@ private JScrollPane createGeneralTab() { layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(dicePanel) - .addComponent(ratPanel)); + layout.setVerticalGroup(layout.createSequentialGroup().addComponent(dicePanel).addComponent(ratPanel)); - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(dicePanel) - .addComponent(ratPanel)); + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(dicePanel) + .addComponent(ratPanel)); return new JScrollPaneWithSpeed(panel); } @@ -623,8 +634,11 @@ private JPanel createDicePanel() { gbc.gridx++; panel.add(getLblTotalDiceResult(), gbc); - final JButton btnDiceRoll = new MMButton("btnDiceRoll", resources, "btnDiceRoll.text", - "btnDiceRoll.toolTipText", evt -> performDiceRoll()); + final JButton btnDiceRoll = new MMButton("btnDiceRoll", + resources, + "btnDiceRoll.text", + "btnDiceRoll.toolTipText", + evt -> performDiceRoll()); gbc.gridx = maxGridX; panel.add(btnDiceRoll, gbc); @@ -701,7 +715,7 @@ private JPanel createRATPanel() { final DefaultComboBoxModel factionModel = new DefaultComboBoxModel<>(); factionModel.addAll(FactionDisplay.getSortedValidFactionDisplays(Factions.getInstance().getFactions(), - (getPerson() == null) ? getGUI().getCampaign().getLocalDate() : getPerson().getDateOfBirth())); + (getPerson() == null) ? getGUI().getCampaign().getLocalDate() : getPerson().getDateOfBirth())); setComboRATFaction(new MMComboBox<>("comboRATFaction", factionModel)); getComboRATFaction().setSelectedIndex(0); gbc.gridx++; @@ -720,8 +734,9 @@ private JPanel createRATPanel() { setComboUnitType(new MMComboBox<>("comboUnitType", unitTypeModel)); getComboUnitType().addItemListener(ev -> { final int unitType = getComboUnitType().getSelectedIndex(); - getComboUnitWeight().setEnabled((unitType == UnitType.MEK) || (unitType == UnitType.TANK) - || (unitType == UnitType.AEROSPACEFIGHTER)); + getComboUnitWeight().setEnabled((unitType == UnitType.MEK) || + (unitType == UnitType.TANK) || + (unitType == UnitType.AEROSPACEFIGHTER)); }); gbc.gridx++; panel.add(getComboUnitType(), gbc); @@ -746,15 +761,21 @@ public void mouseClicked(final MouseEvent evt) { gbc.gridwidth = maxGridX - (getGUI().getCampaign().isGM() ? 2 : 1); panel.add(getLblUnitPicked(), gbc); - final JButton btnRollRAT = new MMButton("btnRollRAT", resources, "btnRollRAT.text", - "btnRollRAT.toolTipText", evt -> setLastRolledUnit(performRATRoll())); + final JButton btnRollRAT = new MMButton("btnRollRAT", + resources, + "btnRollRAT.text", + "btnRollRAT.toolTipText", + evt -> setLastRolledUnit(performRATRoll())); gbc.gridx = getGUI().getCampaign().isGM() ? maxGridX - 1 : maxGridX; gbc.gridwidth = 1; panel.add(btnRollRAT, gbc); if (getGUI().getCampaign().isGM()) { - final JButton btnAddUnit = new MMButton("btnAddUnit", resources, "btnAddUnit.text", - "btnAddUnit.toolTipText", evt -> addRATRolledUnit()); + final JButton btnAddUnit = new MMButton("btnAddUnit", + resources, + "btnAddUnit.text", + "btnAddUnit.toolTipText", + evt -> addRATRolledUnit()); gbc.gridx++; panel.add(btnAddUnit, gbc); } @@ -782,19 +803,17 @@ private JScrollPane createNamesTab() { layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(namePanel) - .addComponent(callsignPanel) - .addComponent(companyNamePanel) - .addComponent(bloodnamePanel)); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(namePanel) + .addComponent(callsignPanel) + .addComponent(companyNamePanel) + .addComponent(bloodnamePanel)); - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(namePanel) - .addComponent(callsignPanel) - .addComponent(companyNamePanel) - .addComponent(bloodnamePanel)); + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(namePanel) + .addComponent(callsignPanel) + .addComponent(companyNamePanel) + .addComponent(bloodnamePanel)); return new JScrollPaneWithSpeed(namesPanel); } @@ -847,7 +866,7 @@ private JPanel createNamePanel() { final DefaultComboBoxModel factionModel = new DefaultComboBoxModel<>(); factionModel.addAll(FactionDisplay.getSortedValidFactionDisplays(Factions.getInstance().getFactions(), - (getPerson() == null) ? getGUI().getCampaign().getLocalDate() : getPerson().getDateOfBirth())); + (getPerson() == null) ? getGUI().getCampaign().getLocalDate() : getPerson().getDateOfBirth())); setComboNameGeneratorFaction(new MMComboBox<>("comboRATFaction", factionModel)); getComboNameGeneratorFaction().setSelectedIndex(0); gbc.gridx++; @@ -860,8 +879,8 @@ private JPanel createNamePanel() { } setComboEthnicCode(new MMComboBox<>("comboEthnicCode", historicalEthnicityModel)); getComboEthnicCode().setSelectedIndex(0); - getComboEthnicCode().addActionListener( - evt -> getComboNameGeneratorFaction().setEnabled(getComboEthnicCode().getSelectedIndex() == 0)); + getComboEthnicCode().addActionListener(evt -> getComboNameGeneratorFaction().setEnabled(getComboEthnicCode().getSelectedIndex() == + 0)); gbc.gridx++; panel.add(getComboEthnicCode(), gbc); @@ -889,9 +908,9 @@ private JPanel createNamePanel() { gbc.gridx++; } - final JLabel lblNameGenerated = new JLabel(resources.getString((getPerson() == null) - ? "lblNamesGenerated.text" - : "lblNameGenerated.text")); + final JLabel lblNameGenerated = new JLabel(resources.getString((getPerson() == null) ? + "lblNamesGenerated.text" : + "lblNameGenerated.text")); lblNameGenerated.setName((getPerson() == null) ? "lblNamesGenerated" : "lblNameGenerated"); panel.add(lblNameGenerated, gbc); @@ -906,19 +925,28 @@ private JPanel createNamePanel() { gbc.gridx = maxGridX - 1; panel.add(getSpnNameNumber(), gbc); - final JButton btnGenerateNames = new MMButton("btnGenerateNames", resources, - "btnGenerateNames.text", "btnGenerateNames.toolTipText", evt -> generateNames()); + final JButton btnGenerateNames = new MMButton("btnGenerateNames", + resources, + "btnGenerateNames.text", + "btnGenerateNames.toolTipText", + evt -> generateNames()); gbc.gridx++; panel.add(btnGenerateNames, gbc); } else { - final JButton btnAssignName = new MMButton("btnAssignName", resources, - "btnAssignName.text", "btnAssignName.toolTipText", evt -> assignName()); + final JButton btnAssignName = new MMButton("btnAssignName", + resources, + "btnAssignName.text", + "btnAssignName.toolTipText", + evt -> assignName()); gbc.gridx = maxGridX - 1; gbc.gridy++; panel.add(btnAssignName, gbc); - final JButton btnGenerateName = new MMButton("btnGenerateName", resources, - "btnGenerateName.text", "btnGenerateName.toolTipText", evt -> generateName()); + final JButton btnGenerateName = new MMButton("btnGenerateName", + resources, + "btnGenerateName.text", + "btnGenerateName.toolTipText", + evt -> generateName()); gbc.gridx++; panel.add(btnGenerateName, gbc); } @@ -965,9 +993,9 @@ private JPanel createCallsignPanel() { gbc.gridx++; } - final JLabel lblCallsignGenerated = new JLabel(resources.getString((getPerson() == null) - ? "lblCallsignsGenerated.text" - : "lblCallsignGenerated.text")); + final JLabel lblCallsignGenerated = new JLabel(resources.getString((getPerson() == null) ? + "lblCallsignsGenerated.text" : + "lblCallsignGenerated.text")); lblCallsignGenerated.setName((getPerson() == null) ? "lblCallsignsGenerated" : "lblCallsignGenerated"); panel.add(lblCallsignGenerated, gbc); @@ -984,19 +1012,28 @@ private JPanel createCallsignPanel() { gbc.gridx++; panel.add(getSpnCallsignNumber(), gbc); - final JButton btnGenerateCallsigns = new MMButton("btnGenerateCallsigns", resources, - "btnGenerateCallsigns.text", "btnGenerateCallsigns.toolTipText", evt -> generateCallsigns()); + final JButton btnGenerateCallsigns = new MMButton("btnGenerateCallsigns", + resources, + "btnGenerateCallsigns.text", + "btnGenerateCallsigns.toolTipText", + evt -> generateCallsigns()); gbc.gridx++; panel.add(btnGenerateCallsigns, gbc); } else { - final JButton btnAssignCallsign = new MMButton("btnAssignCallsign", resources, - "btnAssignCallsign.text", "btnAssignCallsign.toolTipText", evt -> assignCallsign()); + final JButton btnAssignCallsign = new MMButton("btnAssignCallsign", + resources, + "btnAssignCallsign.text", + "btnAssignCallsign.toolTipText", + evt -> assignCallsign()); gbc.gridx = maxGridX - 1; gbc.gridy++; panel.add(btnAssignCallsign, gbc); - final JButton btnGenerateCallsign = new MMButton("btnGenerateCallsign", resources, - "btnGenerateCallsign.text", "btnGenerateCallsign.toolTipText", evt -> generateCallsign()); + final JButton btnGenerateCallsign = new MMButton("btnGenerateCallsign", + resources, + "btnGenerateCallsign.text", + "btnGenerateCallsign.toolTipText", + evt -> generateCallsign()); gbc.gridx++; panel.add(btnGenerateCallsign, gbc); } @@ -1014,8 +1051,7 @@ private void addComponent(JPanel panel, Component component, GridBagConstraints } /** - * Creates and returns a JPanel containing components related to the generation - * of random company names. + * Creates and returns a JPanel containing components related to the generation of random company names. */ private JPanel createCompanyName() { // Create the Panel @@ -1057,43 +1093,39 @@ private JPanel createCompanyName() { } /** - * Creates a MMButton object that generates a company name and updates the - * appropriate JTextArea with the generated name. + * Creates a MMButton object that generates a company name and updates the appropriate JTextArea with the generated + * name. * - * @return a MMButton object that generates a company name and updates the given - * JTextArea + * @return a MMButton object that generates a company name and updates the given JTextArea */ private MMButton createGenerateNameButton() { return new MMButton("btnGenerateCompanyName", - resources, - "btnGenerateCompanyName.text", - "btnGenerateCompanyName.toolTipText", - evt -> { - lastGeneratedCompanyName = randomMercenaryCompanyNameGenerator( - gui.getCampaign().getFlaggedCommander()); - txtCompanyNamesGenerated.setText(lastGeneratedCompanyName); - }); + resources, + "btnGenerateCompanyName.text", + "btnGenerateCompanyName.toolTipText", + evt -> { + lastGeneratedCompanyName = randomMercenaryCompanyNameGenerator(gui.getCampaign() + .getFlaggedCommander()); + txtCompanyNamesGenerated.setText(lastGeneratedCompanyName); + }); } /** - * Creates an instance of MMButton with the label "btnAssignCompanyName" and - * sets the resource bundle, - * text key, tooltip text key, and event handler for the button. + * Creates an instance of MMButton with the label "btnAssignCompanyName" and sets the resource bundle, text key, + * tooltip text key, and event handler for the button. * * @return an instance of MMButton with the specified properties */ private MMButton createAssignCompanyNameButton() { - return new MMButton( - "btnAssignCompanyName", - resources, - "btnAssignCompanyName.text", - "btnAssignCompanyName.toolTipText", - this::assignCompanyName); + return new MMButton("btnAssignCompanyName", + resources, + "btnAssignCompanyName.text", + "btnAssignCompanyName.toolTipText", + this::assignCompanyName); } /** - * Assigns the company name to the campaign and origin force based on certain - * conditions. + * Assigns the company name to the campaign and origin force based on certain conditions. * * @param evt the ActionEvent associated with the button click */ @@ -1138,7 +1170,7 @@ private JPanel createBloodnamePanel() { final DefaultComboBoxModel originClanModel = new DefaultComboBoxModel<>(); originClanModel.addAll(ClanDisplay.getSortedClanDisplays(Clan.getClans(), - getGUI().getCampaign().getLocalDate())); + getGUI().getCampaign().getLocalDate())); setComboOriginClan(new MMComboBox<>("comboOriginClan", originClanModel)); getComboOriginClan().setSelectedIndex(0); getComboOriginClan().addActionListener(evt -> validateBloodnameInput()); @@ -1161,12 +1193,12 @@ private JPanel createBloodnamePanel() { getComboPhenotype().setSelectedItem(Phenotype.GENERAL); getComboPhenotype().setRenderer(new DefaultListCellRenderer() { @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { + public Component getListCellRendererComponent(final JList list, final Object value, final int index, + final boolean isSelected, final boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - setText((value == null) ? "" - : ((value instanceof Phenotype) ? ((Phenotype) value).getGroupingName() : "ERROR")); + setText((value == null) ? + "" : + ((value instanceof Phenotype) ? ((Phenotype) value).getLabel() : "ERROR")); return this; } }); @@ -1231,15 +1263,21 @@ public Component getListCellRendererComponent(final JList list, final Object gbc.gridwidth = maxGridX - ((getPerson() == null) ? 1 : 2); panel.add(getLblBloodnameWarning(), gbc); - final JButton btnGenerateBloodname = new MMButton("btnGenerateBloodname", resources, - "btnGenerateBloodname.text", "btnGenerateBloodname.toolTipText", evt -> generateBloodname()); + final JButton btnGenerateBloodname = new MMButton("btnGenerateBloodname", + resources, + "btnGenerateBloodname.text", + "btnGenerateBloodname.toolTipText", + evt -> generateBloodname()); gbc.gridx = maxGridX - ((getPerson() == null) ? 0 : 1); gbc.gridwidth = 1; panel.add(btnGenerateBloodname, gbc); if (getPerson() != null) { - final JButton btnAssignBloodname = new MMButton("btnAssignBloodname", resources, - "btnAssignBloodname.text", "btnAssignBloodname.toolTipText", evt -> assignBloodname()); + final JButton btnAssignBloodname = new MMButton("btnAssignBloodname", + resources, + "btnAssignBloodname.text", + "btnAssignBloodname.toolTipText", + evt -> assignBloodname()); gbc.gridx++; panel.add(btnAssignBloodname, gbc); } @@ -1262,21 +1300,17 @@ private JScrollPane createPersonnelModuleTab() { final JPanel procreationPanel = createProcreationPanel(); // Layout the Panel - final AbstractMHQScrollablePanel personnelModulePanel = new DefaultMHQScrollablePanel( - getFrame(), "personnelModulePanel"); + final AbstractMHQScrollablePanel personnelModulePanel = new DefaultMHQScrollablePanel(getFrame(), + "personnelModulePanel"); final GroupLayout layout = new GroupLayout(personnelModulePanel); personnelModulePanel.setLayout(layout); layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(procreationPanel)); + layout.setVerticalGroup(layout.createSequentialGroup().addComponent(procreationPanel)); - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(procreationPanel)); + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING).addComponent(procreationPanel)); return new JScrollPaneWithSpeed(personnelModulePanel); } @@ -1309,13 +1343,15 @@ private JPanel createProcreationPanel() { panel.add(lblEligibility, gbc); setChkProcreationEligibilityType(new JCheckBox(resources.getString("chkProcreationEligibilityType.text"))); - getChkProcreationEligibilityType() - .setToolTipText(resources.getString("chkProcreationEligibilityType.toolTipText")); + getChkProcreationEligibilityType().setToolTipText(resources.getString( + "chkProcreationEligibilityType.toolTipText")); getChkProcreationEligibilityType().setName("chkProcreationEligibilityType"); getChkProcreationEligibilityType().addActionListener(evt -> { - final String reason = getGUI().getCampaign().getProcreation().canProcreate( - getGUI().getCampaign().getLocalDate(), getPerson(), - getChkProcreationEligibilityType().isSelected()); + final String reason = getGUI().getCampaign() + .getProcreation() + .canProcreate(getGUI().getCampaign().getLocalDate(), + getPerson(), + getChkProcreationEligibilityType().isSelected()); lblEligibility.setText(resources.getString((reason == null) ? "True.text" : "False.text")); lblEligibility.setToolTipText(reason); }); @@ -1329,8 +1365,8 @@ private JPanel createProcreationPanel() { } final JPanel procreationSimulationPanel = new JPanel(); - procreationSimulationPanel - .setBorder(BorderFactory.createTitledBorder(resources.getString("procreationSimulationPanel.title"))); + procreationSimulationPanel.setBorder(BorderFactory.createTitledBorder(resources.getString( + "procreationSimulationPanel.title"))); procreationSimulationPanel.setToolTipText(resources.getString("procreationSimulationPanel.toolTipText")); procreationSimulationPanel.setName("procreationSimulationPanel"); @@ -1413,9 +1449,9 @@ private void setValuesFromPerson() { } } - final Clan clan = Clan.getClan((getGUI().getCampaign().getFaction().isClan() - ? getGUI().getCampaign().getFaction() - : getPerson().getOriginFaction()).getShortName()); + final Clan clan = Clan.getClan((getGUI().getCampaign().getFaction().isClan() ? + getGUI().getCampaign().getFaction() : + getPerson().getOriginFaction()).getShortName()); if (clan != null) { getComboOriginClan().setSelectedItem(new ClanDisplay(clan, getGUI().getCampaign().getLocalDate())); } @@ -1434,8 +1470,8 @@ private boolean doesPersonPrimarilyDriveUnitType(final int unitType) { case UnitType.BATTLE_ARMOR: return getPerson().getPrimaryRole().isBattleArmour(); case UnitType.CONV_FIGHTER: - return getPerson().getPrimaryRole().isConventionalAircraftPilot() - || getPerson().getPrimaryRole().isAerospacePilot(); + return getPerson().getPrimaryRole().isConventionalAircraftPilot() || + getPerson().getPrimaryRole().isAerospacePilot(); case UnitType.DROPSHIP: case UnitType.JUMPSHIP: case UnitType.SMALL_CRAFT: @@ -1462,9 +1498,10 @@ private boolean doesPersonPrimarilyDriveUnitType(final int unitType) { // region ActionEvent Handlers public void performDiceRoll() { final List individualDice = Compute.individualRolls((Integer) getSpnDiceCount().getValue(), - (Integer) getSpnDiceNumber().getValue(), (Integer) getSpnDiceSides().getValue()); - getLblTotalDiceResult() - .setText(String.format(resources.getString("lblTotalDiceResult.text"), individualDice.get(0))); + (Integer) getSpnDiceNumber().getValue(), + (Integer) getSpnDiceSides().getValue()); + getLblTotalDiceResult().setText(String.format(resources.getString("lblTotalDiceResult.text"), + individualDice.get(0))); final StringBuilder sb = new StringBuilder(); for (int i = 1; i < individualDice.size() - 1; i++) { @@ -1484,17 +1521,32 @@ public void performDiceRoll() { return null; } - final Predicate predicate = summary -> (!getGUI().getCampaign().getCampaignOptions().isLimitByYear() - || (targetYear > summary.getYear())) - && (!summary.isClan() || getGUI().getCampaign().getCampaignOptions().isAllowClanPurchases()) - && (summary.isClan() || getGUI().getCampaign().getCampaignOptions().isAllowISPurchases()); + final Predicate predicate = summary -> (!getGUI().getCampaign() + .getCampaignOptions() + .isLimitByYear() || + (targetYear > summary.getYear())) && + (!summary.isClan() || + getGUI().getCampaign() + .getCampaignOptions() + .isAllowClanPurchases()) && + (summary.isClan() || + getGUI().getCampaign() + .getCampaignOptions() + .isAllowISPurchases()); final int unitType = UnitType.determineUnitTypeCode(getComboUnitType().getSelectedItem()); - final int unitWeight = getComboUnitWeight().isEnabled() - ? getComboUnitWeight().getSelectedIndex() + EntityWeightClass.WEIGHT_LIGHT - : AtBDynamicScenarioFactory.UNIT_WEIGHT_UNSPECIFIED; - final MekSummary summary = getGUI().getCampaign().getUnitGenerator() - .generate(Objects.requireNonNull(getComboRATFaction().getSelectedItem()).getFaction().getShortName(), - unitType, unitWeight, targetYear, getComboQuality().getSelectedIndex(), predicate); + final int unitWeight = getComboUnitWeight().isEnabled() ? + getComboUnitWeight().getSelectedIndex() + EntityWeightClass.WEIGHT_LIGHT : + AtBDynamicScenarioFactory.UNIT_WEIGHT_UNSPECIFIED; + final MekSummary summary = getGUI().getCampaign() + .getUnitGenerator() + .generate(Objects.requireNonNull(getComboRATFaction().getSelectedItem()) + .getFaction() + .getShortName(), + unitType, + unitWeight, + targetYear, + getComboQuality().getSelectedIndex(), + predicate); if (summary == null) { getLblUnitPicked().setText(Messages.getString("noValidUnit.error")); @@ -1507,7 +1559,8 @@ public void performDiceRoll() { return entity; } catch (Exception ex) { final String message = String.format(Messages.getString("entityLoadFailure.error"), - summary.getName(), summary.getSourceFile()); + summary.getName(), + summary.getSourceFile()); logger.error(message, ex); getLblUnitPicked().setText(message); return null; @@ -1558,13 +1611,16 @@ private String[] generateIndividualName() { final String[] name; if (ethnicCode == 0) { - name = RandomNameGenerator.getInstance().generateGivenNameSurnameSplit( - getComboGender().getSelectedItem(), getChkClanPersonnel().isSelected(), - (Objects.requireNonNull(getComboNameGeneratorFaction().getSelectedItem())) - .getFaction().getShortName()); + name = RandomNameGenerator.getInstance() + .generateGivenNameSurnameSplit(getComboGender().getSelectedItem(), + getChkClanPersonnel().isSelected(), + (Objects.requireNonNull(getComboNameGeneratorFaction().getSelectedItem())).getFaction() + .getShortName()); } else { - name = RandomNameGenerator.getInstance().generateGivenNameSurnameSplitWithEthnicCode( - getComboGender().getSelectedItem(), getChkClanPersonnel().isSelected(), ethnicCode); + name = RandomNameGenerator.getInstance() + .generateGivenNameSurnameSplitWithEthnicCode(getComboGender().getSelectedItem(), + getChkClanPersonnel().isSelected(), + ethnicCode); } return name; } @@ -1610,11 +1666,12 @@ private void assignCallsign() { private void generateBloodname() { final Bloodname bloodname = Bloodname.randomBloodname(getOriginClan(), - getSelectedPhenotype(), getBloodnameYear()); + getSelectedPhenotype(), + getBloodnameYear()); if (bloodname != null) { getLblBloodnameGenerated().setText(bloodname.getName() + " (" + bloodname.getFounder() + ')'); getLblOriginClanGenerated().setText(bloodname.getOriginClan().getFullName(getBloodnameYear())); - getLblPhenotypeGenerated().setText(bloodname.getPhenotype().getGroupingName()); + getLblPhenotypeGenerated().setText(bloodname.getPhenotype().getLabel()); setLastGeneratedBloodname(bloodname.getName()); } } @@ -1632,13 +1689,13 @@ private void assignBloodname() { } private void validateBloodnameInput() { - setOriginClan((getComboOriginClan().getSelectedItem() == null) ? null - : getComboOriginClan().getSelectedItem().getClan()); + setOriginClan((getComboOriginClan().getSelectedItem() == null) ? + null : + getComboOriginClan().getSelectedItem().getClan()); setBloodnameYear(BLOODNAME_ERAS[getComboBloodnameEra().getSelectedIndex()]); setSelectedPhenotype(getComboPhenotype().getSelectedItem()); - if ((getOriginClan() == null) || (getSelectedPhenotype() == null) - || getSelectedPhenotype().isNone()) { + if ((getOriginClan() == null) || (getSelectedPhenotype() == null) || getSelectedPhenotype().isNone()) { return; } @@ -1648,8 +1705,13 @@ private void validateBloodnameInput() { for (int era : BLOODNAME_ERAS) { if (era >= getOriginClan().getStartDate()) { setBloodnameYear(era); - txt += "
" + getOriginClan().getFullName(getBloodnameYear()) + " formed in " - + getOriginClan().getStartDate() + ". Using " + getBloodnameYear() + ".
"; + txt += "
" + + getOriginClan().getFullName(getBloodnameYear()) + + " formed in " + + getOriginClan().getStartDate() + + ". Using " + + getBloodnameYear() + + ".
"; break; } } @@ -1661,8 +1723,13 @@ private void validateBloodnameInput() { for (int i = BLOODNAME_ERAS.length - 1; i >= 0; i--) { if (BLOODNAME_ERAS[i] <= getOriginClan().getEndDate()) { setBloodnameYear(BLOODNAME_ERAS[i]); - txt += "
" + getOriginClan().getFullName(getBloodnameYear()) + " ceased to existed in " - + getOriginClan().getEndDate() + ". Using " + getBloodnameYear() + ".
"; + txt += "
" + + getOriginClan().getFullName(getBloodnameYear()) + + " ceased to existed in " + + getOriginClan().getEndDate() + + ". Using " + + getBloodnameYear() + + ".
"; break; } } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/enums/PhenotypeTest.java b/MekHQ/unittests/mekhq/campaign/personnel/enums/PhenotypeTest.java index d0ae9e055b4..40456e2db25 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/enums/PhenotypeTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/enums/PhenotypeTest.java @@ -27,163 +27,73 @@ */ package mekhq.campaign.personnel.enums; +import static mekhq.utilities.MHQInternationalization.isResourceKeyValid; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Arrays; import java.util.List; -import java.util.ResourceBundle; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; -import mekhq.MekHQ; - class PhenotypeTest { - // region Variable Declarations private static final Phenotype[] phenotypes = Phenotype.values(); - private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel", - MekHQ.getMHQOptions().getLocale()); - // endregion Variable Declarations - - // region Getters - @Test - void testGetName() { - assertEquals(resources.getString("Phenotype.AEROSPACE.text"), Phenotype.AEROSPACE.getName()); - assertEquals(resources.getString("Phenotype.NONE.text"), Phenotype.NONE.getName()); - } - @Test - void testGetToolTipText() { - assertEquals(resources.getString("Phenotype.NAVAL.toolTipText"), - Phenotype.NAVAL.getToolTipText()); - assertEquals(resources.getString("Phenotype.NONE.toolTipText"), - Phenotype.NONE.getToolTipText()); - } - // endregion Getters - - // region Boolean Comparison Methods - @Test - void testIsMekWarrior() { - for (final Phenotype phenotype : phenotypes) { - if (phenotype == Phenotype.MEKWARRIOR) { - assertTrue(phenotype.isMekWarrior()); - } else { - assertFalse(phenotype.isMekWarrior()); - } - } + void testFromString() { + // Valid inputs + assertEquals(Phenotype.MEKWARRIOR, Phenotype.fromString("MEKWARRIOR")); + assertEquals(Phenotype.ELEMENTAL, Phenotype.fromString("Elemental")); + assertEquals(Phenotype.AEROSPACE, Phenotype.fromString("aerospace")); + assertEquals(Phenotype.GENERAL, Phenotype.fromString("GENERAL")); + + // Deprecated names + assertEquals(Phenotype.MEKWARRIOR, Phenotype.fromString("MechWarrior")); + assertEquals(Phenotype.PROTOMEK, Phenotype.fromString("Protomech")); + + // Index input + assertEquals(Phenotype.VEHICLE, Phenotype.fromString("3")); + + // Invalid inputs + assertEquals(Phenotype.NONE, Phenotype.fromString("test")); + assertEquals(Phenotype.NONE, Phenotype.fromString("")); + assertEquals(Phenotype.NONE, Phenotype.fromString(null)); } @Test - void testIsElemental() { + void testGetLabel() { for (final Phenotype phenotype : phenotypes) { - if (phenotype == Phenotype.ELEMENTAL) { - assertTrue(phenotype.isElemental()); - } else { - assertFalse(phenotype.isElemental()); - } + String label = phenotype.getLabel(); + boolean isValid = isResourceKeyValid(label); + assertTrue(isValid, "Invalid resource key: " + label); } } @Test - void testIsAerospace() { + void testGetTooltip() { for (final Phenotype phenotype : phenotypes) { - if (phenotype == Phenotype.AEROSPACE) { - assertTrue(phenotype.isAerospace()); - } else { - assertFalse(phenotype.isAerospace()); - } + String tooltip = phenotype.getTooltip(); + boolean isValid = isResourceKeyValid(tooltip); + assertTrue(isValid, "Invalid resource key: " + tooltip); } } @Test - void testIsVehicle() { + void testGetShortName() { for (final Phenotype phenotype : phenotypes) { - if (phenotype == Phenotype.VEHICLE) { - assertTrue(phenotype.isVehicle()); - } else { - assertFalse(phenotype.isVehicle()); - } + String shortName = phenotype.getShortName(); + boolean isValid = isResourceKeyValid(shortName); + assertTrue(isValid, "Invalid resource key: " + shortName); } } - @Test - void testIsProtoMek() { - for (final Phenotype phenotype : phenotypes) { - if (phenotype == Phenotype.PROTOMEK) { - assertTrue(phenotype.isProtoMek()); - } else { - assertFalse(phenotype.isProtoMek()); - } - } - } - - @Test - void testIsNaval() { - for (final Phenotype phenotype : phenotypes) { - if (phenotype == Phenotype.NAVAL) { - assertTrue(phenotype.isNaval()); - } else { - assertFalse(phenotype.isNaval()); - } - } - } - - @Test - void testIsNone() { - for (final Phenotype phenotype : phenotypes) { - if (phenotype == Phenotype.NONE) { - assertTrue(phenotype.isNone()); - } else { - assertFalse(phenotype.isNone()); - } - } - } - - @Test - void testIsGeneral() { - for (final Phenotype phenotype : phenotypes) { - if (phenotype == Phenotype.GENERAL) { - assertTrue(phenotype.isGeneral()); - } else { - assertFalse(phenotype.isGeneral()); - } - } - } - // endregion Boolean Comparison Methods - @Test void testGetExternalPhenotypes() { final List expected = Arrays.stream(phenotypes) - .filter(phenotype -> (phenotype != Phenotype.NONE) && (phenotype != Phenotype.GENERAL)) - .collect(Collectors.toList()); + .filter(phenotype -> (phenotype != Phenotype.NONE) && + (phenotype != Phenotype.GENERAL)) + .collect(Collectors.toList()); assertEquals(expected, Phenotype.getExternalPhenotypes()); } - - // region File I/O - @Test - void testParseFromString() { - // Normal Parsing - assertEquals(Phenotype.NONE, Phenotype.parseFromString("NONE")); - assertEquals(Phenotype.NAVAL, Phenotype.parseFromString("NAVAL")); - - // Error Case - assertEquals(Phenotype.NONE, Phenotype.parseFromString("5")); - assertEquals(Phenotype.NONE, Phenotype.parseFromString("blah")); - } - // endregion File I/O - - @Test - void testToStringOverride() { - assertEquals(resources.getString("Freeborn.text"), Phenotype.NONE.toString()); - assertEquals(resources.getString("Trueborn.text"), Phenotype.GENERAL.toString()); - assertEquals(resources.getString("Trueborn.text") + ' ' + resources.getString("Phenotype.MEKWARRIOR.text"), - Phenotype.MEKWARRIOR.toString()); - assertEquals( - resources.getString("Trueborn.text") + ' ' - + resources.getString("Phenotype.AEROSPACE.groupingNameText"), - Phenotype.AEROSPACE.toString()); - } } From c0dabc0caa20c4102c07865ee56e95479bcc4d94 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 6 Apr 2025 01:55:08 -0500 Subject: [PATCH 02/10] - Adjusted return tag descriptions in Javadocs for clarity and consistency by formatting component names with `{@code}`. --- MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java b/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java index 9439f19b693..4e90525dd70 100644 --- a/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java +++ b/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java @@ -209,7 +209,8 @@ public String getShortName() { * the current instance (as returned by {@code name()}) and uses it to fetch the formatted text from the resource * bundle.

* - * @return A formatted label string corresponding to the key ".label" from the resource bundle. + * @return A formatted label string corresponding to the key "{@code Component Name}.label" from the resource + * bundle. * * @author Illiani * @since 0.50.05 @@ -225,7 +226,8 @@ public String getLabel() { * the current instance (as returned by {@code name()}) and uses it to fetch the formatted text from the resource * bundle.

* - * @return A formatted tooltip string corresponding to the key ".tooltip" from the resource bundle. + * @return A formatted tooltip string corresponding to the key "{@code Component Name}.tooltip" from the resource + * bundle. * * @author Illiani * @since 0.50.05 From 25cfc82208e5fcbb0deff28a3c9328bcb8f8012e Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 6 Apr 2025 02:07:51 -0500 Subject: [PATCH 03/10] Enhanced Special Ability Generation With Phenotype Traits - Added phenotype-based SPA generation by incorporating bonus traits from a character's phenotype. - Imported `LVL3_ADVANTAGES` and adjusted logic to acquire phenotype traits as abilities. - Refactored minor formatting issues for improved readability. --- .../DefaultSpecialAbilityGenerator.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSpecialAbilityGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSpecialAbilityGenerator.java index 044cb12e7c0..b8978b07a1b 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSpecialAbilityGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSpecialAbilityGenerator.java @@ -27,14 +27,18 @@ */ package mekhq.campaign.personnel.generator; +import static megamek.common.options.PilotOptions.LVL3_ADVANTAGES; + +import java.util.List; + import mekhq.Utilities; import mekhq.campaign.Campaign; import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.enums.Phenotype; public class DefaultSpecialAbilityGenerator extends AbstractSpecialAbilityGenerator { @Override - public boolean generateSpecialAbilities(final Campaign campaign, final Person person, - final int expLvl) { + public boolean generateSpecialAbilities(final Campaign campaign, final Person person, final int expLvl) { if (campaign.getCampaignOptions().isUseAbilities()) { SingleSpecialAbilityGenerator singleSpecialAbilityGenerator = new SingleSpecialAbilityGenerator(); singleSpecialAbilityGenerator.setSkillPreferences(getSkillPreferences()); @@ -44,11 +48,21 @@ public boolean generateSpecialAbilities(final Campaign campaign, final Person pe // Then we generate up to that number, stopping if there are no potential // abilities to generate - while ((numAbilities > 0) - && singleSpecialAbilityGenerator.generateSpecialAbilities(campaign, person, expLvl)) { + while ((numAbilities > 0) && + singleSpecialAbilityGenerator.generateSpecialAbilities(campaign, person, expLvl)) { numAbilities--; } + // Finally, we add any SPAs from the character's phenotype + Phenotype phenotype = person.getPhenotype(); + if (phenotype != null) { + List bonusTraits = phenotype.getBonusTraits(); + + for (String bonusTrait : bonusTraits) { + person.getOptions().acquireAbility(LVL3_ADVANTAGES, bonusTrait, true); + } + } + return true; } From a0c7e5f909dfdd58e8b2566492b0e1b012f17168 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 6 Apr 2025 02:15:58 -0500 Subject: [PATCH 04/10] - Consolidated basic and extra randomization under a single streamlined condition block. --- .../generator/DefaultSkillGenerator.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java index d56464e4e55..93f815e54e4 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java @@ -174,27 +174,24 @@ public void generateAttributes(Person person) { attributeModifier += phenotype.getAttributeModifier(attribute); person.changeAttributeScore(attribute, attributeModifier); - // Basic Attribute randomization + // Attribute randomization int roll = d6(); if (roll == 1) { person.changeAttributeScore(attribute, -1); + + if (extraRandomAttributes) { + while ((d6() == 1) && (person.getAttributeScore(attribute) > MINIMUM_ATTRIBUTE_SCORE)) { + person.changeAttributeScore(attribute, -1); + } + } } else if (roll == 6) { person.changeAttributeScore(attribute, 1); - } - - // Extra Attribute randomness - if (extraRandomAttributes) { - roll = d6(); - if (roll == 1) { - do { - person.changeAttributeScore(attribute, -1); - } while ((d6() == 1) && (person.getAttributeScore(attribute) > MINIMUM_ATTRIBUTE_SCORE)); - } else if (roll == 6) { - do { + if (extraRandomAttributes) { + while ((d6() == 6) && (person.getAttributeScore(attribute) < MAXIMUM_ATTRIBUTE_SCORE)) { person.changeAttributeScore(attribute, 1); - } while ((d6() == 1) && (person.getAttributeScore(attribute) < MAXIMUM_ATTRIBUTE_SCORE)); + } } } } From ce079e3a5abbe09e7076fabcb7b42ecade76c348 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 6 Apr 2025 17:26:16 -0500 Subject: [PATCH 05/10] - Replaced `getName` method calls with `getLabel` to improve clarity and align with localized key usage. - Refactored and standardized string formatting across affected files for improved readability. - Removed hardcoded personnel role strings from `Personnel.properties` and introduced `PersonnelRole.properties` to centralize localization resources. - Replaced `parseFromString` method with `fromString` for improved method naming and expanded functionality, including handling deprecated names and case variations. - Added unit tests to validate new `getLabel` functionality, `fromString` method, and missing resource keys for roles. - Updated XML generation and parsing methods to utilize the new role localization handling with `getLabel`. --- .../mekhq/resources/Personnel.properties | 31 - .../mekhq/resources/PersonnelRole.properties | 31 + .../src/mekhq/campaign/MercRosterAccess.java | 230 +++-- .../campaign/market/PersonnelMarket.java | 69 +- .../market/enums/UnitMarketRarity.java | 5 +- .../src/mekhq/campaign/personnel/Person.java | 8 +- .../personnel/enums/PersonnelRole.java | 360 ++++---- .../generator/RandomPortraitGenerator.java | 46 +- .../campaign/report/PersonnelReport.java | 55 +- .../storypoint/CreateCharacterStoryPoint.java | 33 +- .../stratcon/StratconContractInitializer.java | 18 +- MekHQ/src/mekhq/gui/CampaignGUI.java | 3 +- .../immersiveDialogs/ImmersiveDialogCore.java | 4 +- .../contents/AdvancementTab.java | 4 +- MekHQ/src/mekhq/gui/dialog/BatchXPDialog.java | 2 +- .../gui/dialog/HireBulkPersonnelDialog.java | 2 +- .../gui/dialog/PersonnelMarketDialog.java | 131 +-- .../panels/CompanyGenerationOptionsPanel.java | 860 +++++++++--------- .../personnel/enums/PersonnelRoleTest.java | 83 +- 19 files changed, 1031 insertions(+), 944 deletions(-) create mode 100644 MekHQ/resources/mekhq/resources/PersonnelRole.properties diff --git a/MekHQ/resources/mekhq/resources/Personnel.properties b/MekHQ/resources/mekhq/resources/Personnel.properties index becaffe85b5..d1e4ca0607e 100644 --- a/MekHQ/resources/mekhq/resources/Personnel.properties +++ b/MekHQ/resources/mekhq/resources/Personnel.properties @@ -275,37 +275,6 @@ MergingSurnameStyle.FEMALE.dropDownText=Both share the female spouse's surname ( MergingSurnameStyle.WEIGHTED.text=Weighted MergingSurnameStyle.WEIGHTED.toolTipText=The surname style used will be randomly determined through set weights MergingSurnameStyle.WEIGHTED.dropDownText=Surname style randomly determined through set weights -# PersonnelRole Enum -PersonnelRole.MEKWARRIOR.text=MekWarrior -PersonnelRole.LAM_PILOT.text=LAM Pilot -PersonnelRole.GROUND_VEHICLE_DRIVER.text=Vehicle Driver -PersonnelRole.NAVAL_VEHICLE_DRIVER.text=Naval Driver -PersonnelRole.VTOL_PILOT.text=VTOL Pilot -PersonnelRole.VEHICLE_GUNNER.text=Vehicle Gunner -PersonnelRole.VEHICLE_CREW.text=Vehicle Crewmember -PersonnelRole.AEROSPACE_PILOT.text=Aerospace Pilot -PersonnelRole.CONVENTIONAL_AIRCRAFT_PILOT.text=Conventional Aircraft Pilot -PersonnelRole.PROTOMEK_PILOT.text=ProtoMek Pilot -PersonnelRole.BATTLE_ARMOUR.text=Battle Armor Pilot -PersonnelRole.BATTLE_ARMOUR.clan.text=Elemental -PersonnelRole.SOLDIER.text=Soldier -PersonnelRole.VESSEL_PILOT.text=Vessel Pilot -PersonnelRole.VESSEL_GUNNER.text=Vessel Gunner -PersonnelRole.VESSEL_CREW.text=Vessel Crewmember -PersonnelRole.VESSEL_NAVIGATOR.text=Hyperspace Navigator -PersonnelRole.MEK_TECH.text=Mek Tech -PersonnelRole.MECHANIC.text=Mechanic -PersonnelRole.AERO_TEK.text=Aerospace Tech -PersonnelRole.BA_TECH.text=Battle Armor Tech -PersonnelRole.ASTECH.text=Astech -PersonnelRole.DOCTOR.text=Doctor -PersonnelRole.MEDIC.text=Medic -PersonnelRole.ADMINISTRATOR_COMMAND.text=Admin/Command -PersonnelRole.ADMINISTRATOR_LOGISTICS.text=Admin/Logistical -PersonnelRole.ADMINISTRATOR_TRANSPORT.text=Admin/Transport -PersonnelRole.ADMINISTRATOR_HR.text=Admin/HR -PersonnelRole.DEPENDENT.text=Dependent -PersonnelRole.NONE.text=None # Profession Enum Profession.MEKWARRIOR.text=MekWarrior Profession.MEKWARRIOR.toolTipText=The MekWarrior Profession contains MekWarriors, LAM Pilots, and ProtoMek Pilots. diff --git a/MekHQ/resources/mekhq/resources/PersonnelRole.properties b/MekHQ/resources/mekhq/resources/PersonnelRole.properties new file mode 100644 index 00000000000..5195d5dd47a --- /dev/null +++ b/MekHQ/resources/mekhq/resources/PersonnelRole.properties @@ -0,0 +1,31 @@ +# suppress inspection "UnusedProperty" for the whole file +MEKWARRIOR.label=MekWarrior +LAM_PILOT.label=LAM Pilot +GROUND_VEHICLE_DRIVER.label=Vehicle Driver +NAVAL_VEHICLE_DRIVER.label=Naval Driver +VTOL_PILOT.label=VTOL Pilot +VEHICLE_GUNNER.label=Vehicle Gunner +VEHICLE_CREW.label=Vehicle Crewmember +AEROSPACE_PILOT.label=Aerospace Pilot +CONVENTIONAL_AIRCRAFT_PILOT.label=Conventional Aircraft Pilot +PROTOMEK_PILOT.label=ProtoMek Pilot +BATTLE_ARMOUR.label=Battle Armor Pilot +BATTLE_ARMOUR.label.clan=Elemental +SOLDIER.label=Soldier +VESSEL_PILOT.label=Vessel Pilot +VESSEL_GUNNER.label=Vessel Gunner +VESSEL_CREW.label=Vessel Crewmember +VESSEL_NAVIGATOR.label=Hyperspace Navigator +MEK_TECH.label=Mek Tech +MECHANIC.label=Mechanic +AERO_TEK.label=Aerospace Tech +BA_TECH.label=Battle Armor Tech +ASTECH.label=Astech +DOCTOR.label=Doctor +MEDIC.label=Medic +ADMINISTRATOR_COMMAND.label=Admin/Command +ADMINISTRATOR_LOGISTICS.label=Admin/Logistical +ADMINISTRATOR_TRANSPORT.label=Admin/Transport +ADMINISTRATOR_HR.label=Admin/HR +DEPENDENT.label=Dependent +NONE.label=None diff --git a/MekHQ/src/mekhq/campaign/MercRosterAccess.java b/MekHQ/src/mekhq/campaign/MercRosterAccess.java index 0d9a974d6c8..9b0160b415c 100644 --- a/MekHQ/src/mekhq/campaign/MercRosterAccess.java +++ b/MekHQ/src/mekhq/campaign/MercRosterAccess.java @@ -46,8 +46,6 @@ import mekhq.campaign.force.Force; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.PersonnelOptions; -import mekhq.campaign.personnel.skills.Skill; -import mekhq.campaign.personnel.skills.SkillType; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.enums.Profession; import mekhq.campaign.personnel.ranks.Rank; @@ -157,8 +155,9 @@ private void writeBasicData() { statement.execute("TRUNCATE TABLE " + table + ".ranks"); int i = 0; for (Rank rank : campaign.getRankSystem().getRanks()) { - preparedStatement = connect - .prepareStatement("INSERT INTO " + table + ".ranks (number, rankname) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".ranks (number, rankname) VALUES (?, ?)"); preparedStatement.setInt(1, i); // TODO: This currently only exports MekWarrior Ranks. MercRoster software needs // adjusted before this can be. @@ -177,8 +176,9 @@ private void writeBasicData() { try { statement.execute("TRUNCATE TABLE " + table + ".skilltypes"); for (int i = 0; i < SkillType.skillList.length; i++) { - preparedStatement = connect - .prepareStatement("INSERT INTO " + table + ".skilltypes (name, shortname) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skilltypes (name, shortname) VALUES (?, ?)"); preparedStatement.setString(1, truncateString(SkillType.skillList[i], 60)); preparedStatement.setString(2, truncateString(getShortSkillName(SkillType.skillList[i]), 60)); preparedStatement.executeUpdate(); @@ -201,208 +201,237 @@ private void writeBasicData() { int equipment = 0; switch (role) { case MEKWARRIOR: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_PILOT_MEK)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_GUN_MEK)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case LAM_PILOT: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_PILOT_MEK)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_GUN_MEK)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_PILOT_AERO)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_GUN_AERO)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case GROUND_VEHICLE_DRIVER: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_PILOT_GVEE)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case NAVAL_VEHICLE_DRIVER: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_PILOT_NVEE)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case VTOL_PILOT: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_PILOT_VTOL)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case VEHICLE_GUNNER: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_GUN_VEE)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case VEHICLE_CREW: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_TECH_MECHANIC)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case AEROSPACE_PILOT: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_PILOT_AERO)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_GUN_AERO)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case CONVENTIONAL_AIRCRAFT_PILOT: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_PILOT_JET)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_GUN_JET)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case PROTOMEK_PILOT: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_GUN_PROTO)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case BATTLE_ARMOUR: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_GUN_BA)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case SOLDIER: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_SMALL_ARMS)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case VESSEL_PILOT: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_PILOT_SPACE)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case VESSEL_GUNNER: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_GUN_SPACE)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case VESSEL_CREW: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_TECH_VESSEL)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case VESSEL_NAVIGATOR: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_NAV)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); equipment = 1; break; case MEK_TECH: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_TECH_MEK)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); break; case AERO_TEK: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_TECH_AERO)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); break; case MECHANIC: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_TECH_MECHANIC)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); break; case BA_TECH: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_TECH_BA)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); break; case ASTECH: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_ASTECH)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); break; case DOCTOR: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_DOCTOR)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); break; case MEDIC: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_MEDTECH)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); @@ -411,8 +440,9 @@ private void writeBasicData() { case ADMINISTRATOR_HR: case ADMINISTRATOR_LOGISTICS: case ADMINISTRATOR_TRANSPORT: - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skillrequirements (skilltype, personneltype) VALUES (?, ?)"); preparedStatement.setInt(1, skillHash.get(SkillType.S_ADMIN)); preparedStatement.setInt(2, role.ordinal()); preparedStatement.executeUpdate(); @@ -422,9 +452,10 @@ private void writeBasicData() { default: break; } - preparedStatement = connect.prepareStatement("INSERT INTO " + table - + ".crewtypes (type, squad, vehicletype, prefpos, equipment) VALUES (?, ?, ?, ?, ?)"); - preparedStatement.setString(1, truncateString(role.getName(campaign.getFaction().isClan()), 45)); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".crewtypes (type, squad, vehicletype, prefpos, equipment) VALUES (?, ?, ?, ?, ?)"); + preparedStatement.setString(1, truncateString(role.getLabel(campaign.getFaction().isClan()), 45)); preparedStatement.setInt(2, 0); preparedStatement.setInt(3, 1); preparedStatement.setInt(4, role.ordinal()); @@ -448,8 +479,9 @@ private void writeBasicData() { int weightstep = 5; String weightscale = "ton"; // TODO: get these right for various unit types - preparedStatement = connect.prepareStatement("INSERT INTO " + table - + ".equipmenttypes (id, name, license, maxweight, minweight, weightstep, weightscale, prefpos, used, requirement) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".equipmenttypes (id, name, license, maxweight, minweight, weightstep, weightscale, prefpos, used, requirement) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); preparedStatement.setInt(1, i + 1); preparedStatement.setString(2, truncateString(UnitType.getTypeDisplayableName(i), 45)); preparedStatement.setInt(3, i + 1); @@ -474,8 +506,9 @@ private void writeForceData() { // clear the table and re-enter a top level command try { statement.execute("TRUNCATE TABLE " + table + ".unit"); - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".unit (type, name, parent, prefpos, text) VALUES (?, ?, ?, ?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".unit (type, name, parent, prefpos, text) VALUES (?, ?, ?, ?, ?)"); preparedStatement.setString(1, "1"); preparedStatement.setString(2, "Command"); preparedStatement.setInt(3, Integer.MAX_VALUE); @@ -503,8 +536,9 @@ private void writeForceData() { private void writeForce(Force force, int parent) { try { - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".unit (type, name, parent, prefpos, text) VALUES (?, ?, ?, ?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".unit (type, name, parent, prefpos, text) VALUES (?, ?, ?, ?, ?)"); preparedStatement.setString(1, "1"); preparedStatement.setString(2, force.getName()); preparedStatement.setInt(3, parent); @@ -563,8 +597,9 @@ private void writePersonnelData() { forceId = forceHash.get(person.getId()); } try { - preparedStatement = connect.prepareStatement("UPDATE " + table - + ".crew SET rank=?, lname=?, fname=?, callsign=?, status=?, parent=?, crewnumber=?, joiningdate=?, notes=?, bday=? WHERE uuid=?"); + preparedStatement = connect.prepareStatement("UPDATE " + + table + + ".crew SET rank=?, lname=?, fname=?, callsign=?, status=?, parent=?, crewnumber=?, joiningdate=?, notes=?, bday=? WHERE uuid=?"); preparedStatement.setInt(1, person.getRankNumeric()); preparedStatement.setString(2, truncateString(person.getSurname(), 30)); preparedStatement.setString(3, truncateString(person.getGivenName(), 30)); @@ -580,8 +615,9 @@ private void writePersonnelData() { preparedStatement.setString(11, person.getId().toString()); if (preparedStatement.executeUpdate() < 1) { // no prior record so insert - preparedStatement = connect.prepareStatement("INSERT INTO " + table - + ".crew (rank, lname, fname, callsign, status, parent, crewnumber, joiningdate, notes, bday, uuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".crew (rank, lname, fname, callsign, status, parent, crewnumber, joiningdate, notes, bday, uuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); preparedStatement.setInt(1, person.getRankNumeric()); preparedStatement.setString(2, truncateString(person.getSurname(), 30)); preparedStatement.setString(3, truncateString(person.getGivenName(), 30)); @@ -606,8 +642,9 @@ private void writePersonnelData() { // put id in a hash for equipment assignment personHash.put(person.getId(), id); // assign the personnel position - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".personnelpositions (personneltype, person) VALUES (?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".personnelpositions (personneltype, person) VALUES (?, ?)"); preparedStatement.setInt(1, person.getPrimaryRole().ordinal()); preparedStatement.setInt(2, id); preparedStatement.executeUpdate(); @@ -617,8 +654,9 @@ private void writePersonnelData() { for (int i = 0; i < SkillType.skillList.length; i++) { if (person.hasSkill(SkillType.skillList[i])) { Skill skill = person.getSkill(SkillType.skillList[i]); - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".skills (person, skill, value) VALUES (?, ?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".skills (person, skill, value) VALUES (?, ?, ?)"); preparedStatement.setInt(1, id); preparedStatement.setInt(2, i + 1); preparedStatement.setInt(3, skill.getFinalSkillValue(options, reputation)); @@ -629,8 +667,9 @@ private void writePersonnelData() { // FIXME: the only issue here is we get duplicate kills for crewed vehicles // TODO: clean up the getWhatKilled string for (Kill k : campaign.getKillsFor(person.getId())) { - preparedStatement = connect.prepareStatement( - "INSERT INTO " + table + ".kills (parent, type, killdate, equipment) VALUES (?, ?, ?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".kills (parent, type, killdate, equipment) VALUES (?, ?, ?, ?)"); preparedStatement.setInt(1, id); preparedStatement.setString(2, truncateString(k.getWhatKilled(), 45)); preparedStatement.setDate(3, Date.valueOf(k.getDate().toString())); @@ -664,8 +703,9 @@ private void writeEquipmentData() { determineProgress(); for (Unit u : campaign.getHangar().getUnits()) { try { - preparedStatement = connect.prepareStatement("UPDATE " + table - + ".equipment SET type=?, name=?, subtype=?, crew=?, weight=?, regnumber=?, notes=? WHERE uuid=?"); + preparedStatement = connect.prepareStatement("UPDATE " + + table + + ".equipment SET type=?, name=?, subtype=?, crew=?, weight=?, regnumber=?, notes=? WHERE uuid=?"); preparedStatement.setInt(1, u.getEntity().getUnitType() + 1); preparedStatement.setString(2, truncateString(u.getEntity().getChassis(), 45)); preparedStatement.setString(3, truncateString(u.getEntity().getModel(), 45)); @@ -680,8 +720,9 @@ private void writeEquipmentData() { preparedStatement.setString(8, u.getId().toString()); if (preparedStatement.executeUpdate() < 1) { // no prior record so insert - preparedStatement = connect.prepareStatement("INSERT INTO " + table - + ".equipment (type, name, subtype, crew, weight, regnumber, notes, uuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); + preparedStatement = connect.prepareStatement("INSERT INTO " + + table + + ".equipment (type, name, subtype, crew, weight, regnumber, notes, uuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); preparedStatement.setInt(1, 1); preparedStatement.setString(2, truncateString(u.getEntity().getChassis(), 45)); preparedStatement.setString(3, truncateString(u.getEntity().getModel(), 45)); @@ -757,10 +798,13 @@ public String getProgressNote() { } private int getLengthOfTask() { - return 2 + campaign.getRankSystem().getRanks().size() + SkillType.skillList.length - + PersonnelRole.values().length * 2 + UnitType.SIZE + campaign.getPersonnel().size() * - +campaign.getHangar().getUnits().size() - + campaign.getAllForces().size() * 2; + return 2 + + campaign.getRankSystem().getRanks().size() + + SkillType.skillList.length + + PersonnelRole.values().length * 2 + + UnitType.SIZE + + campaign.getPersonnel().size() * +campaign.getHangar().getUnits().size() + + campaign.getAllForces().size() * 2; } public void determineProgress() { diff --git a/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java b/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java index 0c374139dff..11c4011ba11 100644 --- a/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java +++ b/MekHQ/src/mekhq/campaign/market/PersonnelMarket.java @@ -51,7 +51,6 @@ import mekhq.campaign.event.MarketNewPersonnelEvent; import mekhq.campaign.event.OptionsChangedEvent; import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.skills.SkillType; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.skills.SkillType; import mekhq.campaign.rating.IUnitRating; @@ -115,9 +114,9 @@ public void handleCampaignOptionsEvent(OptionsChangedEvent ev) { } /** - * Generates new personnel for the current day, adding them to the campaign's personnel market if applicable. - * The method handles removing outdated personnel, checking hiring hall and capital conditions, - * and updating the personnel pool. + * Generates new personnel for the current day, adding them to the campaign's personnel market if applicable. The + * method handles removing outdated personnel, checking hiring hall and capital conditions, and updating the + * personnel pool. *

* The process includes: *

    @@ -129,8 +128,8 @@ public void handleCampaignOptionsEvent(OptionsChangedEvent ev) { *
  • Optionally generating a personnel market report, if this is enabled in campaign options.
  • *
* - * @param campaign The {@link Campaign} related to the current gameplay context. Used to - * determine the campaign's current planetary system, date, settings, factions, and more. + * @param campaign The {@link Campaign} related to the current gameplay context. Used to determine the campaign's + * current planetary system, date, settings, factions, and more. */ public void generatePersonnelForDay(Campaign campaign) { PlanetarySystem location = campaign.getLocation().getCurrentSystem(); @@ -140,8 +139,9 @@ public void generatePersonnelForDay(Campaign campaign) { boolean isOnPlanet = campaign.getLocation().isOnPlanet(); boolean useCapitalsHiringHallsOnly = campaign.getCampaignOptions().isUsePersonnelHireHiringHallOnly(); boolean isHiringHall = location.isHiringHall(today); - boolean isCapital = location.getFactionSet(today).stream() - .anyMatch(faction -> location.equals(faction.getStartingPlanet(campaign, today))); + boolean isCapital = location.getFactionSet(today) + .stream() + .anyMatch(faction -> location.equals(faction.getStartingPlanet(campaign, today))); // Remove existing personnel for the day if (!personnel.isEmpty()) { @@ -181,9 +181,9 @@ public void generatePersonnelForDay(Campaign campaign) { /** * Generates and adds a report to the campaign about new personnel added to the personnel market. *

- * If the corresponding option is enabled in the campaign settings, this function produces a detailed, - * user-facing report about the most notable individual in the new personnel pool. The report includes - * their experience level, primary role, and name. + * If the corresponding option is enabled in the campaign settings, this function produces a detailed, user-facing + * report about the most notable individual in the new personnel pool. The report includes their experience level, + * primary role, and name. *

* The generated report is in HTML format and provides easy access to the personnel market interface. * @@ -208,13 +208,13 @@ private void generatePersonnelReport(Campaign campaign) { } report.append("") - .append(SkillType.getColoredExperienceLevelName(experienceLevel)) - .append(' ') - .append(person.getPrimaryRole()) - .append("") - .append(" named ") - .append(person.getFullName()) - .append(" is available."); + .append(SkillType.getColoredExperienceLevelName(experienceLevel)) + .append(' ') + .append(person.getPrimaryRole()) + .append("") + .append(" named ") + .append(person.getFullName()) + .append(" is available."); } campaign.addReport(report.toString()); @@ -280,6 +280,7 @@ public void addAttachedEntity(UUID pid, Entity en) { * Get the Entity associated with a recruit, if any * * @param p The recruit + * * @return The Entity associated with the recruit, or null if there is none */ public Entity getAttachedEntity(Person p) { @@ -290,6 +291,7 @@ public Entity getAttachedEntity(Person p) { * Get the Entity associated with a recruit, if any * * @param pid The id of the recruit + * * @return The Entity associated with the recruit, or null if there is none */ public Entity getAttachedEntity(UUID pid) { @@ -337,8 +339,12 @@ public void writeToXML(final PrintWriter pw, int indent, final Campaign campaign MHQXMLUtility.writeSimpleXMLTag(pw, indent, "paidRecruitType", getPaidRecruitRole().name()); for (UUID id : attachedEntities.keySet()) { - MHQXMLUtility.writeSimpleXMLAttributedTag(pw, indent, "entity", "id", id, - attachedEntities.get(id).getShortNameRaw()); + MHQXMLUtility.writeSimpleXMLAttributedTag(pw, + indent, + "entity", + "id", + id, + attachedEntities.get(id).getShortNameRaw()); } MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "personnelMarket"); } @@ -376,8 +382,12 @@ public static PersonnelMarket generateInstanceFromXML(Node wn, Campaign c, Versi try { en = new MekFileParser(ms.getSourceFile(), ms.getEntryName()).getEntity(); } catch (EntityLoadingException ex) { - logger.error("Unable to load entity: " + ms.getSourceFile() + ": " + ms.getEntryName() + ": " - + ex.getMessage(), ex); + logger.error("Unable to load entity: " + + ms.getSourceFile() + + ": " + + ms.getEntryName() + + ": " + + ex.getMessage(), ex); } if (null != en) { retVal.attachedEntities.put(id, en); @@ -385,7 +395,7 @@ public static PersonnelMarket generateInstanceFromXML(Node wn, Campaign c, Versi } else if (wn2.getNodeName().equalsIgnoreCase("paidRecruitment")) { retVal.paidRecruitment = true; } else if (wn2.getNodeName().equalsIgnoreCase("paidRecruitType")) { - retVal.setPaidRecruitRole(PersonnelRole.parseFromString(wn2.getTextContent().trim())); + retVal.setPaidRecruitRole(PersonnelRole.fromString(wn2.getTextContent().trim())); } else if (null != retVal.method) { retVal.method.loadFieldsFromXml(wn2); } else { @@ -467,8 +477,8 @@ public static long getUnitMainForceTypes(Campaign c) { int sc = hangarStats.getNumberOfUnitsByType(Entity.ETYPE_SMALL_CRAFT); int cf = hangarStats.getNumberOfUnitsByType(Entity.ETYPE_CONV_FIGHTER); int asf = hangarStats.getNumberOfUnitsByType(Entity.ETYPE_AEROSPACEFIGHTER); - int vee = hangarStats.getNumberOfUnitsByType(Entity.ETYPE_TANK, true) - + hangarStats.getNumberOfUnitsByType(Entity.ETYPE_TANK); + int vee = hangarStats.getNumberOfUnitsByType(Entity.ETYPE_TANK, true) + + hangarStats.getNumberOfUnitsByType(Entity.ETYPE_TANK); int inf = hangarStats.getNumberOfUnitsByType(Entity.ETYPE_INFANTRY); int ba = hangarStats.getNumberOfUnitsByType(Entity.ETYPE_BATTLEARMOR); int proto = hangarStats.getNumberOfUnitsByType(Entity.ETYPE_PROTOMEK); @@ -531,11 +541,12 @@ public static long getUnitMainForceTypes(Campaign c) { public TargetRoll getShipSearchTarget(Campaign campaign, boolean jumpship) { TargetRoll target = new TargetRoll(jumpship ? 12 : 10, "Base"); Person adminLog = campaign.findBestInRole(PersonnelRole.ADMINISTRATOR_LOGISTICS, SkillType.S_ADMIN); - int adminLogExp = (adminLog == null) ? SkillType.EXP_ULTRA_GREEN - : adminLog.getSkill(SkillType.S_ADMIN).getExperienceLevel(); + int adminLogExp = (adminLog == null) ? + SkillType.EXP_ULTRA_GREEN : + adminLog.getSkill(SkillType.S_ADMIN).getExperienceLevel(); for (Person p : campaign.getAdmins()) { - if ((p.getPrimaryRole().isAdministratorLogistics() || p.getSecondaryRole().isAdministratorLogistics()) - && p.getSkill(SkillType.S_ADMIN).getExperienceLevel() > adminLogExp) { + if ((p.getPrimaryRole().isAdministratorLogistics() || p.getSecondaryRole().isAdministratorLogistics()) && + p.getSkill(SkillType.S_ADMIN).getExperienceLevel() > adminLogExp) { adminLogExp = p.getSkill(SkillType.S_ADMIN).getExperienceLevel(); } } diff --git a/MekHQ/src/mekhq/campaign/market/enums/UnitMarketRarity.java b/MekHQ/src/mekhq/campaign/market/enums/UnitMarketRarity.java index d11b6c359ce..7298e89376c 100644 --- a/MekHQ/src/mekhq/campaign/market/enums/UnitMarketRarity.java +++ b/MekHQ/src/mekhq/campaign/market/enums/UnitMarketRarity.java @@ -48,7 +48,7 @@ public enum UnitMarketRarity { // region Constructors UnitMarketRarity(final String name) { final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Market", - MekHQ.getMHQOptions().getLocale()); + MekHQ.getMHQOptions().getLocale()); this.name = resources.getString(name); } // endregion Constructors @@ -93,8 +93,7 @@ public static UnitMarketRarity parseFromString(final String text) { case "3", "Common" -> COMMON; case "4", "Very Common" -> VERY_COMMON; default -> throw new IllegalStateException( - "Unexpected value in mekhq/campaign/market/enums/UnitMarketRarity.java/parseFromString: " - + text); + "Unexpected value in mekhq/campaign/market/enums/UnitMarketRarity.java/fromString: " + text); }; } // endregion File I/O diff --git a/MekHQ/src/mekhq/campaign/personnel/Person.java b/MekHQ/src/mekhq/campaign/personnel/Person.java index b233c0645d1..17a1d6ee168 100644 --- a/MekHQ/src/mekhq/campaign/personnel/Person.java +++ b/MekHQ/src/mekhq/campaign/personnel/Person.java @@ -975,11 +975,11 @@ public String getPrimaryRoleDesc() { if (isClanPersonnel()) { bgPrefix = getPhenotype().getShortName() + ' '; } - return bgPrefix + getPrimaryRole().getName(isClanPersonnel()); + return bgPrefix + getPrimaryRole().getLabel(isClanPersonnel()); } public String getSecondaryRoleDesc() { - return getSecondaryRole().getName(isClanPersonnel()); + return getSecondaryRole().getLabel(isClanPersonnel()); } public boolean canPerformRole(LocalDate today, final PersonnelRole role, final boolean primary) { @@ -2677,10 +2677,10 @@ public static Person generateInstanceFromXML(Node wn, Campaign campaign, Version } else if (nodeName.equalsIgnoreCase("biography")) { person.biography = wn2.getTextContent(); } else if (nodeName.equalsIgnoreCase("primaryRole")) { - final PersonnelRole primaryRole = PersonnelRole.parseFromString(wn2.getTextContent().trim()); + final PersonnelRole primaryRole = PersonnelRole.fromString(wn2.getTextContent().trim()); person.setPrimaryRoleDirect(primaryRole); } else if (nodeName.equalsIgnoreCase("secondaryRole")) { - person.setSecondaryRoleDirect(PersonnelRole.parseFromString(wn2.getTextContent().trim())); + person.setSecondaryRoleDirect(PersonnelRole.fromString(wn2.getTextContent().trim())); } else if (nodeName.equalsIgnoreCase("acquisitions")) { person.acquisitions = MathUtility.parseInt(wn2.getTextContent()); } else if (nodeName.equalsIgnoreCase("primaryDesignator")) { diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java b/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java index d2e80c9e348..b629d9a3b1f 100644 --- a/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java +++ b/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java @@ -27,16 +27,16 @@ */ package mekhq.campaign.personnel.enums; +import static mekhq.utilities.MHQInternationalization.getFormattedTextAt; + import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.List; -import java.util.ResourceBundle; import java.util.stream.Collectors; import java.util.stream.Stream; -import megamek.common.annotations.Nullable; +import megamek.codeUtilities.MathUtility; import megamek.logging.MMLogger; -import mekhq.MekHQ; import mekhq.campaign.personnel.skills.enums.SkillAttribute; /** @@ -47,50 +47,115 @@ public enum PersonnelRole { // region Enum Declarations /** * Individual roles with corresponding name texts and mnemonics. + * + *

Note: The attribute score modifiers assume a default attribute score of 5. I opted to only include + * this information as modifiers, as it means if we ever change what the default score is, the ratios will remain + * correct.

*/ - MEKWARRIOR("PersonnelRole.MEKWARRIOR.text", KeyEvent.VK_M), - LAM_PILOT("PersonnelRole.LAM_PILOT.text", KeyEvent.VK_UNDEFINED), - GROUND_VEHICLE_DRIVER("PersonnelRole.GROUND_VEHICLE_DRIVER.text", KeyEvent.VK_V), - NAVAL_VEHICLE_DRIVER("PersonnelRole.NAVAL_VEHICLE_DRIVER.text", KeyEvent.VK_N), - VTOL_PILOT("PersonnelRole.VTOL_PILOT.text", KeyEvent.VK_UNDEFINED), - VEHICLE_GUNNER("PersonnelRole.VEHICLE_GUNNER.text", KeyEvent.VK_G), - VEHICLE_CREW("PersonnelRole.VEHICLE_CREW.text", KeyEvent.VK_UNDEFINED), - AEROSPACE_PILOT("PersonnelRole.AEROSPACE_PILOT.text", KeyEvent.VK_A), - CONVENTIONAL_AIRCRAFT_PILOT("PersonnelRole.CONVENTIONAL_AIRCRAFT_PILOT.text", KeyEvent.VK_C), - PROTOMEK_PILOT("PersonnelRole.PROTOMEK_PILOT.text", KeyEvent.VK_P), - BATTLE_ARMOUR("PersonnelRole.BATTLE_ARMOUR.text", - "PersonnelRole.BATTLE_ARMOUR.clan.text", - KeyEvent.VK_B, - 0, - 0, - 0, - 0, - 0, - 0, - 0), - SOLDIER("PersonnelRole.SOLDIER.text", KeyEvent.VK_S), - VESSEL_PILOT("PersonnelRole.VESSEL_PILOT.text", KeyEvent.VK_I), - VESSEL_GUNNER("PersonnelRole.VESSEL_GUNNER.text", KeyEvent.VK_U), - VESSEL_CREW("PersonnelRole.VESSEL_CREW.text", KeyEvent.VK_W), - VESSEL_NAVIGATOR("PersonnelRole.VESSEL_NAVIGATOR.text", KeyEvent.VK_Y), - MEK_TECH("PersonnelRole.MEK_TECH.text", KeyEvent.VK_T), - MECHANIC("PersonnelRole.MECHANIC.text", KeyEvent.VK_E), - AERO_TEK("PersonnelRole.AERO_TEK.text", KeyEvent.VK_O), - BA_TECH("PersonnelRole.BA_TECH.text", KeyEvent.VK_UNDEFINED), - ASTECH("PersonnelRole.ASTECH.text", KeyEvent.VK_UNDEFINED), - DOCTOR("PersonnelRole.DOCTOR.text", KeyEvent.VK_D), - MEDIC("PersonnelRole.MEDIC.text", KeyEvent.VK_UNDEFINED), - ADMINISTRATOR_COMMAND("PersonnelRole.ADMINISTRATOR_COMMAND.text", KeyEvent.VK_UNDEFINED), - ADMINISTRATOR_LOGISTICS("PersonnelRole.ADMINISTRATOR_LOGISTICS.text", KeyEvent.VK_L), - ADMINISTRATOR_TRANSPORT("PersonnelRole.ADMINISTRATOR_TRANSPORT.text", KeyEvent.VK_R), - ADMINISTRATOR_HR("PersonnelRole.ADMINISTRATOR_HR.text", KeyEvent.VK_H), - DEPENDENT("PersonnelRole.DEPENDENT.text", KeyEvent.VK_UNDEFINED), - NONE("PersonnelRole.NONE.text", KeyEvent.VK_UNDEFINED); + // I used an average of the modifiers from the MekWarrior, Hot Shot, and Grizzled Veteran ATOW Archetypes + MEKWARRIOR(true, KeyEvent.VK_M, -1, 0, 1, 1, -1, -1, -1), + + // I used an average of the modifiers from the MekWarrior, and Aerospace Pilot ATOW Archetypes + LAM_PILOT(true, KeyEvent.VK_UNDEFINED, -1, -1, 1, 1, -1, -1, -1), + + // ATOW: Tanker Archetype + GROUND_VEHICLE_DRIVER(true, KeyEvent.VK_V, -1, 0, 0, +1, -1, -1, -1), + + // ATOW: Tanker Archetype + NAVAL_VEHICLE_DRIVER(true, KeyEvent.VK_N, -1, 0, 0, +1, -1, -1, -1), + + // ATOW: Companion Chopper Pilot Archetype + VTOL_PILOT(true, KeyEvent.VK_UNDEFINED, -1, -1, 0, 0, -1, -1, -1), + + // ATOW: Tanker Archetype + VEHICLE_GUNNER(true, KeyEvent.VK_G, -1, 0, 0, +1, -1, -1, -1), + + // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the + // Technician skill) + VEHICLE_CREW(true, KeyEvent.VK_UNDEFINED, 0, -1, 0, -2, 0, -1, -2), + + // ATOW: Aerospace Pilot Archetype + AEROSPACE_PILOT(true, KeyEvent.VK_A, -3, -2, 0, 0, -1, -1, 0), + + // ATOW: Aerospace Pilot Archetype + CONVENTIONAL_AIRCRAFT_PILOT(true, KeyEvent.VK_C, -3, -2, 0, 0, -1, -1, 0), + + // ATOW: Aerospace Pilot Archetype (most ProtoMek pilots are Aerospace Sibkbo washouts, so this made the most sense) + PROTOMEK_PILOT(true, KeyEvent.VK_P, -3, -2, 0, 0, -1, -1, 0), + + // ATOW: Elemental Archetype + BATTLE_ARMOUR(true, true, KeyEvent.VK_B, +2, +1, -1, 0, -2, -1, -2), + + // ATOW: Renegade Warrior Archetype + SOLDIER(true, KeyEvent.VK_S, 0, 0, -1, 0, -1, +1, -2), + + // ATOW: Tanker Archetype + VESSEL_PILOT(true, KeyEvent.VK_I, -1, 0, 0, +1, -1, -1, -1), + + // ATOW: Tanker Archetype + VESSEL_GUNNER(true, KeyEvent.VK_U, -1, 0, 0, +1, -1, -1, -1), + + // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the + // Technician skill) + VESSEL_CREW(true, KeyEvent.VK_W, 0, -1, 0, -2, 0, -1, -2), + + // ATOW: Battlefield Tech Archetype + VESSEL_NAVIGATOR(true, KeyEvent.VK_Y, 0, -1, 0, -2, 0, -1, -2), + + // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the + // Technician skill) + MEK_TECH(false, KeyEvent.VK_T, 0, -1, 0, -2, 0, -1, -2), + + // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the + // Technician skill) + MECHANIC(false, KeyEvent.VK_E, 0, -1, 0, -2, 0, -1, -2), + + // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the + // Technician skill) + AERO_TEK(false, KeyEvent.VK_O, 0, -1, 0, -2, 0, -1, -2), + + // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the + // Technician skill) + BA_TECH(false, KeyEvent.VK_UNDEFINED, 0, -1, 0, -2, 0, -1, -2), + + // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the + // Technician skill) + ASTECH(false, KeyEvent.VK_UNDEFINED, 0, -1, 0, -2, 0, -1, -2), + + // ATOW: Communications Specialist Archetype (this might seem like an odd choice, but the Attributes for this Archetype + // work really well for this profession + DOCTOR(false, KeyEvent.VK_D, -2, -1, -1, 0, 1, -1, -1), + + // ATOW: Communications Specialist Archetype (this might seem like an odd choice, but the Attributes for this Archetype + // work really well for this profession + MEDIC(false, KeyEvent.VK_UNDEFINED, -2, -1, -1, 0, 1, -1, -1), + + // ATOW: Faceman Archetype + ADMINISTRATOR_COMMAND(false, KeyEvent.VK_UNDEFINED, -2, -2, -2, -1, 1, -2, 1), + + // ATOW: Faceman Archetype + ADMINISTRATOR_LOGISTICS(false, KeyEvent.VK_L, -2, -2, -2, -1, 1, -2, 1), + + // ATOW: Faceman Archetype + ADMINISTRATOR_TRANSPORT(false, KeyEvent.VK_R, -2, -2, -2, -1, 1, -2, 1), + + // ATOW: Faceman Archetype + ADMINISTRATOR_HR(false, KeyEvent.VK_H, -2, -2, -2, -1, 1, -2, 1), + + // No archetype, but ATOW pg 35 states that the Attribute scores for an average person are 4 + DEPENDENT(false, KeyEvent.VK_UNDEFINED, -1, -1, -1, -1, -1, -1, -1), + + // If we're generating a character without a Profession, we're just going to leave them with middle of the road + // Attribute scores (5 in everything) + NONE(KeyEvent.VK_UNDEFINED); // endregion Enum Declarations // region Variable Declarations - private final String name; - private final String clanName; + private static final MMLogger logger = MMLogger.create(PersonnelRole.class); + private static final String RESOURCE_BUNDLE = "mekhq.resources." + PersonnelRole.class.getSimpleName(); + + private final boolean isCombat; + private final boolean hasClanName; private final int mnemonic; // Unused: J, K, Q, X, Z private final int strength; private final int body; @@ -102,37 +167,25 @@ public enum PersonnelRole { // endregion Variable Declarations // region Constructors + PersonnelRole(final int mnemonic) { + this(false, false, mnemonic, 0, 0, 0, 0, 0, 0, 0); + } - /** - * Constructs a new PersonnelRole with the given name and mnemonic. - * - * @param name the name of the personnel role - * @param mnemonic the mnemonic of the personnel role - */ - PersonnelRole(final String name, final int mnemonic) { - this(name, null, mnemonic, 0, 0, 0, 0, 0, 0, 0); + PersonnelRole(final boolean isCombat, final int mnemonic, final int strength, final int body, final int dexterity, + final int reflexes, final int intelligence, final int willpower, final int charisma) { + this(isCombat, false, mnemonic, strength, body, dexterity, reflexes, intelligence, willpower, charisma); } - /** - * Main constructor that initializes the role with name, clanName, and mnemonic. ClanName is optional and defaults - * to name if {@code null}. - * - * @param name the name of the role. - * @param clanName the clan name of the role can be {@code null}. - * @param mnemonic the mnemonic associated with the role. - */ - PersonnelRole(final String name, @Nullable final String clanName, final int mnemonic, final int strength, - final int body, final int reflexes, final int dexterity, final int intelligence, final int willpower, + PersonnelRole(final boolean isCombat, final boolean hasClanName, final int mnemonic, final int strength, + final int body, final int dexterity, final int reflexes, final int intelligence, final int willpower, final int charisma) { - final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel", - MekHQ.getMHQOptions().getLocale()); - this.name = resources.getString(name); - this.clanName = (clanName == null) ? this.name : resources.getString(clanName); + this.isCombat = isCombat; + this.hasClanName = hasClanName; this.mnemonic = mnemonic; this.strength = strength; this.body = body; - this.reflexes = reflexes; this.dexterity = dexterity; + this.reflexes = reflexes; this.intelligence = intelligence; this.willpower = willpower; this.charisma = charisma; @@ -140,8 +193,34 @@ public enum PersonnelRole { // endregion Constructors // region Getters + + /** + * @deprecated use {@link #getLabel(boolean)} instead + */ + @Deprecated(since = "0.50.05", forRemoval = true) public String getName(final boolean isClan) { - return isClan ? clanName : name; + return getLabel(isClan); + } + + /** + * Retrieves the label for this instance, optionally using a clan-specific label if applicable. + * + *

This method generates a label based on a specific resource bundle key. If the specified + * option to use a clan label is enabled and a clan name is available, it retrieves the clan-specific label. + * Otherwise, it retrieves the standard label.

+ * + * @param isClan A flag indicating whether to use the clan-specific label. If {@code true} and the instance has a + * clan name, the clan-specific label will be used. + * + * @return The formatted label string, either clan-specific or standard, based on the provided flag and + * availability. + * + * @author Illiani + * @since 0.50.05 + */ + public String getLabel(final boolean isClan) { + final boolean useClan = isClan && hasClanName; + return getFormattedTextAt(RESOURCE_BUNDLE, name() + ".label" + (useClan ? ".clan" : "")); } public int getMnemonic() { @@ -395,25 +474,7 @@ public boolean isNone() { * @return {@code true} if the character has a combat role, {@code true} otherwise. */ public boolean isCombat() { - return switch (this) { - case MEKWARRIOR, - LAM_PILOT, - GROUND_VEHICLE_DRIVER, - NAVAL_VEHICLE_DRIVER, - VTOL_PILOT, - VEHICLE_GUNNER, - VEHICLE_CREW, - AEROSPACE_PILOT, - CONVENTIONAL_AIRCRAFT_PILOT, - PROTOMEK_PILOT, - BATTLE_ARMOUR, - SOLDIER, - VESSEL_PILOT, - VESSEL_GUNNER, - VESSEL_CREW, - VESSEL_NAVIGATOR -> true; - default -> false; - }; + return isCombat; } /** @@ -635,87 +696,65 @@ public static int getCivilianCount() { } // endregion Static Methods + // region File I/O + /** - * Parses a string representation of a {@link PersonnelRole} and returns the corresponding enum value. + * @deprecated use {@link #fromString(String)} instead. + */ + @Deprecated(since = "0.50.05", forRemoval = true) + public static PersonnelRole parseFromString(final String personnelRole) { + return fromString(personnelRole); + } + + /** + * Converts a given string into a {@code PersonnelRole}. + * + *

This method attempts to parse the input string into a {@code PersonnelRole} using a series of steps:

+ * + *
    + *
  1. If the input is {@code null} or blank, the method logs an error and returns {@code NONE}.
  2. + *
  3. Tries to parse the input as an enum name by converting it to uppercase and replacing spaces with underscores.
  4. + *
  5. Attempts to match the input string with the labels of available {@code PersonnelRole} values, both standard and clan-specific.
  6. + *
  7. Includes compatibility handling for versions earlier than 50.1 with specific string mappings.
  8. + *
  9. Finally, tries to parse the input as an ordinal value of the enum.
  10. + *
  11. If all attempts fail, the method logs an error and returns {@code NONE}.
  12. + *
* - * @param personnelRole the string representation of the {@link PersonnelRole} + * @param text The input string to be converted into a {@code PersonnelRole}. * - * @return the corresponding {@link PersonnelRole} enum value, or {@code NONE} if parsing fails + * @return The corresponding {@code PersonnelRole} if successfully parsed, or {@code NONE} if parsing fails. + * + * @author Illiani + * @since 0.50.5 */ - // region File I/O - public static PersonnelRole parseFromString(final String personnelRole) { + public static PersonnelRole fromString(String text) { + if (text == null || text.isBlank()) { + logger.error("Unable to parse text into a PersonnelRole. Returning NONE"); + return NONE; + } + + // Parse from name try { - return valueOf(personnelRole); + return PersonnelRole.valueOf(text.toUpperCase().replace(" ", "_")); } catch (Exception ignored) { } - // Magic Number Save Format + // Parse from label try { - switch (Integer.parseInt(personnelRole)) { - case 0: - return NONE; - case 1: - return MEKWARRIOR; - case 2: - return AEROSPACE_PILOT; - case 3: - return GROUND_VEHICLE_DRIVER; - case 4: - return NAVAL_VEHICLE_DRIVER; - case 5: - return VTOL_PILOT; - case 6: - return VEHICLE_GUNNER; - case 7: - return BATTLE_ARMOUR; - case 8: - return SOLDIER; - case 9: - return PROTOMEK_PILOT; - case 10: - return CONVENTIONAL_AIRCRAFT_PILOT; - case 11: - return VESSEL_PILOT; - case 12: - return VESSEL_CREW; - case 13: - return VESSEL_GUNNER; - case 14: - return VESSEL_NAVIGATOR; - case 15: - return MEK_TECH; - case 16: - return MECHANIC; - case 17: - return AERO_TEK; - case 18: - return BA_TECH; - case 19: - return ASTECH; - case 20: - return DOCTOR; - case 21: - return MEDIC; - case 22: - return ADMINISTRATOR_COMMAND; - case 23: - return ADMINISTRATOR_LOGISTICS; - case 24: - return ADMINISTRATOR_TRANSPORT; - case 25: - return ADMINISTRATOR_HR; - case 26: - return LAM_PILOT; - case 27: - return VEHICLE_CREW; - default: - break; + for (PersonnelRole personnelRole : PersonnelRole.values()) { + if (personnelRole.getLabel(false).equalsIgnoreCase(text)) { + return personnelRole; + } + + if (personnelRole.getLabel(true).equalsIgnoreCase(text)) { + return personnelRole; + } } } catch (Exception ignored) { } // <50.1 compatibility - switch (personnelRole) { + switch (text.toUpperCase().replace(" ", "_")) { case "MECHWARRIOR" -> { return MEKWARRIOR; } @@ -732,12 +771,13 @@ public static PersonnelRole parseFromString(final String personnelRole) { } } - // Error report, if parsing fails. - // Ignore IDEA's suggestion of concatenating the error log, as this - // functionality doesn't - // exist within MMLogger - MMLogger.create(PersonnelRole.class) - .error("Unable to parse {} into a PersonnelRole. Returning {}.", personnelRole, NONE); + // Parse from ordinal + try { + return PersonnelRole.values()[MathUtility.parseInt(text, NONE.ordinal())]; + } catch (Exception ignored) { + } + + logger.error("Unable to parse {} into a PersonnelRole. Returning NONE", text); return NONE; } // endregion File I/O @@ -749,6 +789,6 @@ public static PersonnelRole parseFromString(final String personnelRole) { */ @Override public String toString() { - return name; + return getLabel(false); } } diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/RandomPortraitGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/RandomPortraitGenerator.java index c633981c0a7..b9685181893 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/RandomPortraitGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/RandomPortraitGenerator.java @@ -27,15 +27,20 @@ */ package mekhq.campaign.personnel.generator; +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + import megamek.common.Compute; import megamek.common.icons.Portrait; import megamek.logging.MMLogger; import mekhq.MHQStaticDirectoryManager; import mekhq.campaign.personnel.Person; -import java.io.File; -import java.util.*; - public class RandomPortraitGenerator { private static final MMLogger logger = MMLogger.create(RandomPortraitGenerator.class); @@ -46,20 +51,20 @@ private RandomPortraitGenerator() { /** * This generates a unique Portrait based on the supplied {@link Person} * - * @param personnel a list of all personnel, from which existing portraits are - * determined + * @param personnel a list of all personnel, from which existing portraits are determined * @param p the {@link Person} to generate a unique portrait for + * * @return the generated portrait */ - public static Portrait generate(Collection personnel, Person p, - Boolean allowDuplicatePortraits) { + public static Portrait generate(Collection personnel, Person p, Boolean allowDuplicatePortraits) { // first create a list of existing portrait strings, so we can check for // duplicates - unless they are allowed in campaign options Set existingPortraits = new HashSet<>(); if (!allowDuplicatePortraits) { for (Person existingPerson : personnel) { - existingPortraits.add(existingPerson.getPortrait().getCategory() + ':' - + existingPerson.getPortrait().getFilename()); + existingPortraits.add(existingPerson.getPortrait().getCategory() + + ':' + + existingPerson.getPortrait().getFilename()); } } @@ -69,7 +74,7 @@ public static Portrait generate(Collection personnel, Person p, // and if none are found then /gender/rolegroup, then /gender/combat or // /gender/support, then in /gender. File genderFile = new File(p.getGender().isFemale() ? "Female" : "Male"); - File searchFile = new File(genderFile, p.getPrimaryRole().getName(p.isClanPersonnel())); + File searchFile = new File(genderFile, p.getPrimaryRole().getLabel(p.isClanPersonnel())); possiblePortraits = getPossibleRandomPortraits(existingPortraits, searchFile); @@ -108,29 +113,30 @@ public static Portrait generate(Collection personnel, Person p, if (temp.length == 2) { return new Portrait(temp[0], temp[1]); } else { - logger.error("Failed to generate portrait for " + p.getFullTitle() + ". " - + chosenPortrait + " does not split into an array of length 2."); + logger.error("Failed to generate portrait for " + + p.getFullTitle() + + ". " + + chosenPortrait + + " does not split into an array of length 2."); } } else { - logger.warn("Failed to generate portrait for " + p.getFullTitle() - + ". No possible portraits found."); + logger.warn("Failed to generate portrait for " + p.getFullTitle() + ". No possible portraits found."); } return new Portrait(); } /** - * This is a helper method that determines what possible unassigned portraits - * can be generated - * based on the supplied subdirectory + * This is a helper method that determines what possible unassigned portraits can be generated based on the supplied + * subdirectory * - * @param existingPortraits the list of existing portraits that have already - * been assigned + * @param existingPortraits the list of existing portraits that have already been assigned * @param subdirectory the subdirectory to search + * * @return a list of all possible unassigned random portraits */ private static List getPossibleRandomPortraits(final Set existingPortraits, - final File subdirectory) { + final File subdirectory) { if (MHQStaticDirectoryManager.getPortraits() == null) { return new ArrayList<>(); } diff --git a/MekHQ/src/mekhq/campaign/report/PersonnelReport.java b/MekHQ/src/mekhq/campaign/report/PersonnelReport.java index 1a7dbca7f27..effe6fcc1a0 100644 --- a/MekHQ/src/mekhq/campaign/report/PersonnelReport.java +++ b/MekHQ/src/mekhq/campaign/report/PersonnelReport.java @@ -63,8 +63,7 @@ public String getCombatPersonnelDetails() { if (p.getStatus().isActive()) { countPersonByType[p.getPrimaryRole().ordinal()]++; countTotal++; - if (getCampaign().getCampaignOptions().isUseAdvancedMedical() - && !p.getInjuries().isEmpty()) { + if (getCampaign().getCampaignOptions().isUseAdvancedMedical() && !p.getInjuries().isEmpty()) { countInjured++; } else if (p.getHits() > 0) { countInjured++; @@ -93,18 +92,20 @@ public String getCombatPersonnelDetails() { for (PersonnelRole role : personnelRoles) { if (role.isCombat()) { - sb.append(String.format(" %-30s %4s\n", role.getName(getCampaign().getFaction().isClan()), - countPersonByType[role.ordinal()])); + sb.append(String.format(" %-30s %4s\n", + role.getLabel(getCampaign().getFaction().isClan()), + countPersonByType[role.ordinal()])); } } sb.append('\n') - .append(String.format("%-30s %4s\n", "Injured Combat Personnel", countInjured)) - .append(String.format("%-30s %4s\n", "MIA Combat Personnel", countMIA)) - .append(String.format("%-30s %4s\n", "KIA Combat Personnel", countKIA)) - .append(String.format("%-30s %4s\n", "Retired Combat Personnel", countRetired)) - .append(String.format("%-30s %4s\n", "Dead Combat Personnel", countDead)) - .append("\nMonthly Salary For Combat Personnel: ").append(salary.toAmountAndSymbolString()); + .append(String.format("%-30s %4s\n", "Injured Combat Personnel", countInjured)) + .append(String.format("%-30s %4s\n", "MIA Combat Personnel", countMIA)) + .append(String.format("%-30s %4s\n", "KIA Combat Personnel", countKIA)) + .append(String.format("%-30s %4s\n", "Retired Combat Personnel", countRetired)) + .append(String.format("%-30s %4s\n", "Dead Combat Personnel", countDead)) + .append("\nMonthly Salary For Combat Personnel: ") + .append(salary.toAmountAndSymbolString()); return sb.toString(); } @@ -160,10 +161,12 @@ public String getSupportPersonnelDetails() { } //Add Salaries of Temp Workers - salary = salary.plus(getCampaign().getCampaignOptions().getRoleBaseSalaries()[PersonnelRole.ASTECH.ordinal()].getAmount().doubleValue() - * getCampaign().getAstechPool()); - salary = salary.plus(getCampaign().getCampaignOptions().getRoleBaseSalaries()[PersonnelRole.MEDIC.ordinal()].getAmount().doubleValue() - * getCampaign().getMedicPool()); + salary = salary.plus(getCampaign().getCampaignOptions() + .getRoleBaseSalaries()[PersonnelRole.ASTECH.ordinal()].getAmount().doubleValue() * + getCampaign().getAstechPool()); + salary = salary.plus(getCampaign().getCampaignOptions() + .getRoleBaseSalaries()[PersonnelRole.MEDIC.ordinal()].getAmount().doubleValue() * + getCampaign().getMedicPool()); StringBuilder sb = new StringBuilder("Support Personnel\n\n"); @@ -171,8 +174,9 @@ public String getSupportPersonnelDetails() { for (PersonnelRole role : personnelRoles) { if (role.isSupport(true)) { - sb.append(String.format(" %-30s %4s\n", role.getName(getCampaign().getFaction().isClan()), - countPersonByType[role.ordinal()])); + sb.append(String.format(" %-30s %4s\n", + role.getLabel(getCampaign().getFaction().isClan()), + countPersonByType[role.ordinal()])); } } @@ -181,15 +185,16 @@ public String getSupportPersonnelDetails() { sb.append(String.format(" %-30s %4s\n", "Temp Astechs", getCampaign().getAstechPool())); sb.append('\n') - .append(String.format("%-30s %4s\n", "Injured Support Personnel", countInjured)) - .append(String.format("%-30s %4s\n", "MIA Support Personnel", countMIA)) - .append(String.format("%-30s %4s\n", "KIA Support Personnel", countKIA)) - .append(String.format("%-30s %4s\n", "Retired Support Personnel", countRetired)) - .append(String.format("%-30s %4s\n", "Dead Support Personnel", countDead)) - .append("\nMonthly Salary For Support Personnel: ").append(salary.toAmountAndSymbolString()) - .append(String.format("\nYou have " + dependents + " %s", (dependents == 1) ? "dependent" : "dependents")) - .append(String.format("\nYou have " + prisoners + " prisoner%s", prisoners == 1 ? "" : "s")) - .append(String.format("\nYou have " + bondsmen + " %s", (bondsmen == 1) ? "bondsman" : "bondsmen")); + .append(String.format("%-30s %4s\n", "Injured Support Personnel", countInjured)) + .append(String.format("%-30s %4s\n", "MIA Support Personnel", countMIA)) + .append(String.format("%-30s %4s\n", "KIA Support Personnel", countKIA)) + .append(String.format("%-30s %4s\n", "Retired Support Personnel", countRetired)) + .append(String.format("%-30s %4s\n", "Dead Support Personnel", countDead)) + .append("\nMonthly Salary For Support Personnel: ") + .append(salary.toAmountAndSymbolString()) + .append(String.format("\nYou have " + dependents + " %s", (dependents == 1) ? "dependent" : "dependents")) + .append(String.format("\nYou have " + prisoners + " prisoner%s", prisoners == 1 ? "" : "s")) + .append(String.format("\nYou have " + bondsmen + " %s", (bondsmen == 1) ? "bondsman" : "bondsmen")); return sb.toString(); } diff --git a/MekHQ/src/mekhq/campaign/storyarc/storypoint/CreateCharacterStoryPoint.java b/MekHQ/src/mekhq/campaign/storyarc/storypoint/CreateCharacterStoryPoint.java index 57dfba2c3b5..2e87f1933aa 100644 --- a/MekHQ/src/mekhq/campaign/storyarc/storypoint/CreateCharacterStoryPoint.java +++ b/MekHQ/src/mekhq/campaign/storyarc/storypoint/CreateCharacterStoryPoint.java @@ -44,7 +44,6 @@ import mekhq.campaign.force.Force; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.PersonnelOptions; -import mekhq.campaign.personnel.skills.SkillType; import mekhq.campaign.personnel.backgrounds.BackgroundsController; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.enums.Phenotype; @@ -61,10 +60,8 @@ import org.w3c.dom.NodeList; /** - * This StoryPoint opens a {@link CreateCharacterDialog CreateCharacterDialog} - * which allows a player to create a new - * character. Various initial values can be set, as well as an initial - * experience point pool. Additionally, the ability + * This StoryPoint opens a {@link CreateCharacterDialog CreateCharacterDialog} which allows a player to create a new + * character. Various initial values can be set, as well as an initial experience point pool. Additionally, the ability * to edit certain parts of the character can be restricted. */ public class CreateCharacterStoryPoint extends StoryPoint { @@ -88,8 +85,7 @@ public class CreateCharacterStoryPoint extends StoryPoint { private int edge; /** - * The id of the person in the campaign. This will otherwise be set randomly. By - * setting it manually we can + * The id of the person in the campaign. This will otherwise be set randomly. By setting it manually we can * reference it later. */ private UUID personId; @@ -219,9 +215,17 @@ public Person createPerson() { public void start() { super.start(); Person person = createPerson(); - final CreateCharacterDialog personDialog = new CreateCharacterDialog(null, true, person, - getCampaign(), xpPool, instructions, editOrigin, editBirthday, editGender, nameRestrictions, - limitFaction); + final CreateCharacterDialog personDialog = new CreateCharacterDialog(null, + true, + person, + getCampaign(), + xpPool, + instructions, + editOrigin, + editBirthday, + editGender, + nameRestrictions, + limitFaction); getCampaign().importPerson(person); personDialog.setVisible(true); if (null != assignedUnitId) { @@ -244,8 +248,7 @@ private void setEdgeTriggers(Person p) { // role at the moment PersonnelOptions options = p.getOptions(); - for (Enumeration i = options.getGroups(); i - .hasMoreElements();) { + for (Enumeration i = options.getGroups(); i.hasMoreElements(); ) { IOptionGroup group = i.nextElement(); if (!group.getKey().equalsIgnoreCase(PersonnelOptions.EDGE_ADVANTAGES)) { @@ -253,7 +256,7 @@ private void setEdgeTriggers(Person p) { } IOption option; - for (Enumeration j = group.getOptions(); j.hasMoreElements();) { + for (Enumeration j = group.getOptions(); j.hasMoreElements(); ) { option = j.nextElement(); if (null != option && option.getType() == IOption.BOOLEAN) { p.setEdgeTrigger(option.getName(), true); @@ -319,9 +322,9 @@ protected void loadFieldsFromXmlNode(Node wn, Campaign c, Version version) throw } else if (wn2.getNodeName().equalsIgnoreCase("commander")) { commander = Boolean.parseBoolean(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("primaryRole")) { - primaryRole = PersonnelRole.parseFromString(wn2.getTextContent().trim()); + primaryRole = PersonnelRole.fromString(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("phenotype")) { - phenotype = Phenotype.parseFromString(wn2.getTextContent().trim()); + phenotype = Phenotype.fromString(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("faction")) { faction = Factions.getInstance().getFaction(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("editOrigin")) { diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java index 6c042738b7f..bbba5ce0040 100644 --- a/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java +++ b/MekHQ/src/mekhq/campaign/stratcon/StratconContractInitializer.java @@ -65,7 +65,8 @@ public class StratconContractInitializer { /** * Initializes the campaign state given a contract, campaign and contract definition */ - public static void initializeCampaignState(AtBContract contract, Campaign campaign, StratconContractDefinition contractDefinition) { + public static void initializeCampaignState(AtBContract contract, Campaign campaign, + StratconContractDefinition contractDefinition) { StratconCampaignState campaignState = new StratconCampaignState(contract); campaignState.setBriefingText(contractDefinition.getBriefing() + "
" + @@ -272,7 +273,7 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai // // if (hiddenCache != null) { // logger.info(String.format("A secret cache has been spawned for contract %s", - // contract.getName())); + // contract.getLabel())); // } // } else { // logger.error("'Chasing a Rumor' scenario failed to deserialize"); @@ -286,7 +287,8 @@ public static void initializeCampaignState(AtBContract contract, Campaign campai /** * Set up initial state of a track, dimensions are based on number of assigned lances. */ - public static StratconTrackState initializeTrackState(int numLances, int scenarioOdds, int deploymentTime, int planetaryTemp) { + public static StratconTrackState initializeTrackState(int numLances, int scenarioOdds, int deploymentTime, + int planetaryTemp) { // to initialize a track, // 1. we set the # of required lances // 2. set the track size to a total of numlances * 28 hexes, a rectangle that is @@ -350,7 +352,8 @@ private static List trackObjectDistribution(int numObjects, int numTrac * Avoids places with existing facilities and scenarios, capable of taking facility sub set and setting strategic * objective flag. */ - private static void initializeTrackFacilities(StratconTrackState trackState, int numFacilities, ForceAlignment owner, boolean strategicObjective, List modifiers) { + private static void initializeTrackFacilities(StratconTrackState trackState, int numFacilities, + ForceAlignment owner, boolean strategicObjective, List modifiers) { int trackSize = trackState.getWidth() * trackState.getHeight(); @@ -424,7 +427,9 @@ private static void initializeTrackFacilities(StratconTrackState trackState, int * @param objectiveModifiers a list of optional {@link String} modifiers to apply to the generated scenarios; can be * {@code null} if no modifiers are required */ - private static void initializeObjectiveScenarios(Campaign campaign, AtBContract contract, StratconTrackState trackState, int numScenarios, List objectiveScenarios, List objectiveModifiers) { + private static void initializeObjectiveScenarios(Campaign campaign, AtBContract contract, + StratconTrackState trackState, int numScenarios, List objectiveScenarios, + List objectiveModifiers) { // pick scenario from subset // place it on the map somewhere nothing else has been placed yet // if it's a facility scenario, place the facility @@ -551,7 +556,8 @@ private static void initializeObjectiveScenarios(Campaign campaign, AtBContract * @return a {@link StratconCoords} object representing the location of a suitable, unoccupied coordinate, or * {@code null} if no valid coordinates are available */ - public static @Nullable StratconCoords getUnoccupiedCoords(StratconTrackState trackState, boolean allowPlayerFacilities, boolean allowPlayerForces, boolean emphasizeStrategicTargets) { + public static @Nullable StratconCoords getUnoccupiedCoords(StratconTrackState trackState, + boolean allowPlayerFacilities, boolean allowPlayerForces, boolean emphasizeStrategicTargets) { final int trackHeight = trackState.getHeight(); final int trackWidth = trackState.getWidth(); diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index 3200ae13635..b63ac121737 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -92,7 +92,6 @@ import mekhq.campaign.parts.Refit; import mekhq.campaign.parts.enums.PartQuality; import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.skills.SkillType; import mekhq.campaign.personnel.autoAwards.AutoAwardsController; import mekhq.campaign.personnel.divorce.RandomDivorce; import mekhq.campaign.personnel.enums.PersonnelRole; @@ -750,7 +749,7 @@ private void initMenu() { JMenu menuHire = new JMenu(resourceMap.getString("menuHire.text")); menuHire.setMnemonic(KeyEvent.VK_H); for (PersonnelRole role : PersonnelRole.getPrimaryRoles()) { - JMenuItem miHire = new JMenuItem(role.getName(getCampaign().getFaction().isClan())); + JMenuItem miHire = new JMenuItem(role.getLabel(getCampaign().getFaction().isClan())); if (role.getMnemonic() != KeyEvent.VK_UNDEFINED) { miHire.setMnemonic(role.getMnemonic()); miHire.setAccelerator(KeyStroke.getKeyStroke(role.getMnemonic(), InputEvent.ALT_DOWN_MASK)); diff --git a/MekHQ/src/mekhq/gui/baseComponents/immersiveDialogs/ImmersiveDialogCore.java b/MekHQ/src/mekhq/gui/baseComponents/immersiveDialogs/ImmersiveDialogCore.java index fd54260a82a..c5df439b9e8 100644 --- a/MekHQ/src/mekhq/gui/baseComponents/immersiveDialogs/ImmersiveDialogCore.java +++ b/MekHQ/src/mekhq/gui/baseComponents/immersiveDialogs/ImmersiveDialogCore.java @@ -664,12 +664,12 @@ public static StringBuilder getSpeakerDescription(Campaign campaign, Person spea PersonnelRole primaryRole = speaker.getPrimaryRole(); if (!primaryRole.isNone()) { - speakerDescription.append("
").append(primaryRole.getName(isClan)); + speakerDescription.append("
").append(primaryRole.getLabel(isClan)); } PersonnelRole secondaryRole = speaker.getSecondaryRole(); if (!secondaryRole.isNone()) { - speakerDescription.append("
").append(secondaryRole.getName(isClan)); + speakerDescription.append("
").append(secondaryRole.getLabel(isClan)); } Unit assignedUnit = speaker.getUnit(); diff --git a/MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java b/MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java index 78e5869069e..96556ad8cee 100644 --- a/MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java +++ b/MekHQ/src/mekhq/gui/campaignOptions/contents/AdvancementTab.java @@ -1034,7 +1034,7 @@ private JPanel createRecruitmentBonusesCombatPanel() { layout.gridx = currentColumn * 2; layout.gridy = currentRow; - lblRecruitmentBonusCombat[i] = new JLabel(roles.get(i).getName(false)); + lblRecruitmentBonusCombat[i] = new JLabel(roles.get(i).getLabel(false)); spnRecruitmentBonusCombat[i] = new JSpinner(new SpinnerNumberModel(0, -12, 12, 1)); panel.add(lblRecruitmentBonusCombat[i], layout); @@ -1076,7 +1076,7 @@ private JPanel createRecruitmentBonusesSupportPanel() { layout.gridx = currentColumn * 2; layout.gridy = currentRow; - lblRecruitmentBonusSupport[i] = new JLabel(roles.get(i).getName(false)); + lblRecruitmentBonusSupport[i] = new JLabel(roles.get(i).getLabel(false)); spnRecruitmentBonusSupport[i] = new JSpinner(new SpinnerNumberModel(0, -12, 12, 1)); panel.add(lblRecruitmentBonusSupport[i], layout); diff --git a/MekHQ/src/mekhq/gui/dialog/BatchXPDialog.java b/MekHQ/src/mekhq/gui/dialog/BatchXPDialog.java index dd03e44ecdb..192ec12e91c 100644 --- a/MekHQ/src/mekhq/gui/dialog/BatchXPDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/BatchXPDialog.java @@ -170,7 +170,7 @@ private JComponent getButtonPanel() { personTypeModel.addElement(new PersonTypeItem(resourceMap.getString("primaryRole.choice.text"), null)); final PersonnelRole[] personnelRoles = PersonnelRole.values(); for (PersonnelRole personnelRole : personnelRoles) { - personTypeModel.addElement(new PersonTypeItem(personnelRole.getName(campaign.getFaction().isClan()), + personTypeModel.addElement(new PersonTypeItem(personnelRole.getLabel(campaign.getFaction().isClan()), personnelRole.ordinal())); } choiceType.setModel(personTypeModel); diff --git a/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java b/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java index ffda6c9e165..78741fc80e7 100644 --- a/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java @@ -134,7 +134,7 @@ private void initComponents() { DefaultComboBoxModel personTypeModel = new DefaultComboBoxModel<>(); for (final PersonnelRole personnelRole : PersonnelRole.getPrimaryRoles()) { - personTypeModel.addElement(new PersonTypeItem(personnelRole.getName(campaign.getFaction().isClan()), + personTypeModel.addElement(new PersonTypeItem(personnelRole.getLabel(campaign.getFaction().isClan()), personnelRole)); } choiceType.setModel(personTypeModel); diff --git a/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java b/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java index d98c8b10e93..b29f9b159f9 100644 --- a/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/PersonnelMarketDialog.java @@ -89,26 +89,26 @@ public class PersonnelMarketDialog extends JDialog { // region Variable Declarations private PersonnelTableModel personnelModel; - private Campaign campaign; - private CampaignGUI hqView; - private PersonnelMarket personnelMarket; - private Person selectedPerson = null; - private Money unitCost = Money.zero(); - - private JComboBox comboPersonType; - private JLabel lblPersonChoice; - private JRadioButton radioNormalRoll; - private JRadioButton radioPaidRecruitment; - private JComboBox comboRecruitRole; - private JPanel panelOKBtns; - private JPanel panelMain; - private JPanel panelFilterBtns; - private JTable tablePersonnel; - private JLabel lblUnitCost; - private JScrollPane scrollTablePersonnel; - private JScrollPane scrollPersonnelView; + private Campaign campaign; + private CampaignGUI hqView; + private PersonnelMarket personnelMarket; + private Person selectedPerson = null; + private Money unitCost = Money.zero(); + + private JComboBox comboPersonType; + private JLabel lblPersonChoice; + private JRadioButton radioNormalRoll; + private JRadioButton radioPaidRecruitment; + private JComboBox comboRecruitRole; + private JPanel panelOKBtns; + private JPanel panelMain; + private JPanel panelFilterBtns; + private JTable tablePersonnel; + private JLabel lblUnitCost; + private JScrollPane scrollTablePersonnel; + private JScrollPane scrollPersonnelView; private TableRowSorter sorter; - private JSplitPane splitMain; + private JSplitPane splitMain; private final List personnelMarketColumns = List.of(PersonnelTableModelColumn.FIRST_NAME, PersonnelTableModelColumn.LAST_NAME, @@ -125,10 +125,10 @@ public class PersonnelMarketDialog extends JDialog { public PersonnelMarketDialog(final JFrame frame, final CampaignGUI view, final Campaign campaign) { super(frame, true); - hqView = view; - this.campaign = campaign; + hqView = view; + this.campaign = campaign; personnelMarket = campaign.getPersonnelMarket(); - personnelModel = new PersonnelTableModel(campaign); + personnelModel = new PersonnelTableModel(campaign); personnelModel.setData(personnelMarket.getPersonnel()); personnelModel.loadAssignmentFromMarket(personnelMarket); initComponents(); @@ -139,17 +139,17 @@ public PersonnelMarketDialog(final JFrame frame, final CampaignGUI view, final C private void initComponents() { scrollTablePersonnel = new JScrollPaneWithSpeed(); - scrollPersonnelView = new JScrollPaneWithSpeed(); - tablePersonnel = new JTable(); - panelMain = new JPanel(); - panelFilterBtns = new JPanel(); - comboPersonType = new JComboBox<>(); - radioNormalRoll = new JRadioButton(); + scrollPersonnelView = new JScrollPaneWithSpeed(); + tablePersonnel = new JTable(); + panelMain = new JPanel(); + panelFilterBtns = new JPanel(); + comboPersonType = new JComboBox<>(); + radioNormalRoll = new JRadioButton(); radioPaidRecruitment = new JRadioButton(); - lblUnitCost = new JLabel(); - panelOKBtns = new JPanel(); - lblPersonChoice = new JLabel(); - comboRecruitRole = new JComboBox<>(PersonnelRole.values()); + lblUnitCost = new JLabel(); + panelOKBtns = new JPanel(); + lblPersonChoice = new JLabel(); + comboRecruitRole = new JComboBox<>(PersonnelRole.values()); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); setTitle("Personnel Market"); @@ -168,11 +168,11 @@ public void windowClosing(WindowEvent e) { lblPersonChoice.setText("Personnel Type:"); lblPersonChoice.setName("lblPersonChoice"); GridBagConstraints gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; gridBagConstraints.weightx = 0.0; - gridBagConstraints.anchor = GridBagConstraints.WEST; - gridBagConstraints.insets = new Insets(5, 5, 5, 5); + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new Insets(5, 5, 5, 5); panelFilterBtns.add(lblPersonChoice, gridBagConstraints); DefaultComboBoxModel personTypeModel = new DefaultComboBoxModel<>(); @@ -185,28 +185,28 @@ public void windowClosing(WindowEvent e) { comboPersonType.setName("comboUnitType"); comboPersonType.setPreferredSize(new Dimension(200, 27)); comboPersonType.addActionListener(evt -> filterPersonnel()); - gridBagConstraints = new GridBagConstraints(); - gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 0; + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; gridBagConstraints.anchor = GridBagConstraints.WEST; panelFilterBtns.add(comboPersonType, gridBagConstraints); - boolean atbOutofContract = campaign.getCampaignOptions().isUseAtB() && !campaign.hasActiveContract(); + boolean atbOutofContract = campaign.getCampaignOptions().isUseAtB() && !campaign.hasActiveContract(); boolean usingCamOpsMarkets = campaign.getCampaignOptions().getPersonnelMarketName().equals("Campaign Ops"); if (atbOutofContract && !usingCamOpsMarkets) { // Paid recruitment is available radioNormalRoll.setText("Make normal roll next week"); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 1; + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; gridBagConstraints.gridwidth = 2; - gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.anchor = GridBagConstraints.WEST; panelFilterBtns.add(radioNormalRoll, gridBagConstraints); radioPaidRecruitment.setText("Make paid recruitment roll next week (100,000 C-bills)"); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 2; + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; gridBagConstraints.gridwidth = 2; - gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.anchor = GridBagConstraints.WEST; panelFilterBtns.add(radioPaidRecruitment, gridBagConstraints); ButtonGroup group = new ButtonGroup(); @@ -221,18 +221,19 @@ public void windowClosing(WindowEvent e) { final boolean isClan = campaign.getFaction().isClan(); comboRecruitRole.setRenderer(new DefaultListCellRenderer() { @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + public Component getListCellRendererComponent(JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) { return super.getListCellRendererComponent(list, - (value instanceof PersonnelRole) ? ((PersonnelRole) value).getName(isClan) : value, + (value instanceof PersonnelRole) ? ((PersonnelRole) value).getLabel(isClan) : value, index, isSelected, cellHasFocus); } }); - gridBagConstraints.gridx = 2; - gridBagConstraints.gridy = 2; + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 2; gridBagConstraints.gridwidth = 1; - gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.anchor = GridBagConstraints.WEST; panelFilterBtns.add(comboRecruitRole, gridBagConstraints); if (personnelMarket.getPaidRecruitment()) { @@ -261,8 +262,8 @@ public Component getListCellRendererComponent(JList list, Object value, int i sorter = new TableRowSorter<>(personnelModel); - final XTableColumnModel columnModel = (XTableColumnModel) tablePersonnel.getColumnModel(); - final ArrayList sortKeys = new ArrayList<>(); + final XTableColumnModel columnModel = (XTableColumnModel) tablePersonnel.getColumnModel(); + final ArrayList sortKeys = new ArrayList<>(); for (final PersonnelTableModelColumn column : PersonnelTableModel.PERSONNEL_COLUMNS) { final TableColumn tableColumn = columnModel.getColumnByModelIndex(column.ordinal()); if (!personnelMarketColumns.contains(column)) { @@ -383,8 +384,8 @@ private void hirePerson(ActionEvent evt) { selectedPerson.getSalary(campaign).multipliedBy(2) : Money.zero()).plus(unitCost))) { campaign.addReport("Insufficient funds. Transaction cancelled."); + MekHQ.getMHQOptions().getFontColorNegativeHexColor() + + "'>Insufficient funds. Transaction cancelled."); } else { /* * Adding person to campaign changes pid; grab the old one to @@ -407,8 +408,8 @@ private void hirePerson(ActionEvent evt) { private void addPerson() { if (selectedPerson != null) { - Entity en = personnelMarket.getAttachedEntity(selectedPerson); - UUID pid = selectedPerson.getId(); + Entity en = personnelMarket.getAttachedEntity(selectedPerson); + UUID pid = selectedPerson.getId(); if (campaign.recruitPerson(selectedPerson, true)) { addUnit(en, false); @@ -426,11 +427,11 @@ private void addUnit(Entity en, boolean pay) { } if (pay && - !campaign.getFinances() - .debit(TransactionType.UNIT_PURCHASE, - campaign.getLocalDate(), - unitCost, - "Purchased " + en.getShortName())) { + !campaign.getFinances() + .debit(TransactionType.UNIT_PURCHASE, + campaign.getLocalDate(), + unitCost, + "Purchased " + en.getShortName())) { return; } @@ -513,7 +514,7 @@ private void personChanged(ListSelectionEvent evt) { unitCost = Money.zero(); } else { if (!campaign.getCampaignOptions().isUseShareSystem() && - ((en instanceof Mek) || (en instanceof Tank) || (en instanceof Aero))) { + ((en instanceof Mek) || (en instanceof Tank) || (en instanceof Aero))) { unitCost = Money.of(en.getCost(false)).dividedBy(2.0); } else { unitCost = Money.zero(); @@ -532,7 +533,7 @@ void refreshPersonView() { return; } - Entity en = personnelMarket.getAttachedEntity(selectedPerson); + Entity en = personnelMarket.getAttachedEntity(selectedPerson); String unitText = ""; if (unitCost.isPositive()) { unitText = "Unit cost: " + unitCost.toAmountAndSymbolString(); @@ -552,7 +553,7 @@ void refreshPersonView() { if (null != en) { JTabbedPane tabUnit = new JTabbedPane(); - String name = "Commander"; + String name = "Commander"; if (Compute.getFullCrewSize(en) == 1) { name = "Pilot"; } diff --git a/MekHQ/src/mekhq/gui/panels/CompanyGenerationOptionsPanel.java b/MekHQ/src/mekhq/gui/panels/CompanyGenerationOptionsPanel.java index 4a3b3837e3d..4e9e212e59d 100644 --- a/MekHQ/src/mekhq/gui/panels/CompanyGenerationOptionsPanel.java +++ b/MekHQ/src/mekhq/gui/panels/CompanyGenerationOptionsPanel.java @@ -36,7 +36,6 @@ import java.util.Map.Entry; import java.util.Objects; import java.util.TreeMap; - import javax.swing.*; import javax.swing.GroupLayout.Alignment; import javax.swing.JSpinner.NumberEditor; @@ -164,7 +163,7 @@ public class CompanyGenerationOptionsPanel extends AbstractMHQScrollablePanel { // region Constructors public CompanyGenerationOptionsPanel(final JFrame frame, final Campaign campaign, - final @Nullable CompanyGenerationOptions companyGenerationOptions) { + final @Nullable CompanyGenerationOptions companyGenerationOptions) { super(frame, "CompanyGenerationOptionsPanel", new GridBagLayout()); this.campaign = campaign; setTracksViewportWidth(false); @@ -190,7 +189,7 @@ public MMComboBox getComboCompanyGenerationMethod() { } public void setComboCompanyGenerationMethod( - final MMComboBox comboCompanyGenerationMethod) { + final MMComboBox comboCompanyGenerationMethod) { this.comboCompanyGenerationMethod = comboCompanyGenerationMethod; } @@ -257,8 +256,8 @@ public JLabel getLblTotalSupportPersonnel() { } public void updateLblTotalSupportPersonnel(final int numSupportPersonnel) { - getLblTotalSupportPersonnel().setText(String.format( - resources.getString("lblTotalSupportPersonnel.text"), numSupportPersonnel)); + getLblTotalSupportPersonnel().setText(String.format(resources.getString("lblTotalSupportPersonnel.text"), + numSupportPersonnel)); } public void setLblTotalSupportPersonnel(final JLabel lblTotalSupportPersonnel) { @@ -318,7 +317,7 @@ public JCheckBox getChkPrioritizeCompanyCommanderCombatSkills() { } public void setChkPrioritizeCompanyCommanderCombatSkills( - final JCheckBox chkPrioritizeCompanyCommanderCombatSkills) { + final JCheckBox chkPrioritizeCompanyCommanderCombatSkills) { this.chkPrioritizeCompanyCommanderCombatSkills = chkPrioritizeCompanyCommanderCombatSkills; } @@ -429,7 +428,7 @@ public MMComboBox getComboBattleMekFactionGene } public void setComboBattleMekFactionGenerationMethod( - final MMComboBox comboBattleMekFactionGenerationMethod) { + final MMComboBox comboBattleMekFactionGenerationMethod) { this.comboBattleMekFactionGenerationMethod = comboBattleMekFactionGenerationMethod; } @@ -438,7 +437,7 @@ public MMComboBox getComboBattleMekWeightC } public void setComboBattleMekWeightClassGenerationMethod( - final MMComboBox comboBattleMekWeightClassGenerationMethod) { + final MMComboBox comboBattleMekWeightClassGenerationMethod) { this.comboBattleMekWeightClassGenerationMethod = comboBattleMekWeightClassGenerationMethod; } @@ -447,7 +446,7 @@ public MMComboBox getComboBattleMekQualityGene } public void setComboBattleMekQualityGenerationMethod( - final MMComboBox comboBattleMekQualityGenerationMethod) { + final MMComboBox comboBattleMekQualityGenerationMethod) { this.comboBattleMekQualityGenerationMethod = comboBattleMekQualityGenerationMethod; } @@ -554,7 +553,7 @@ public JCheckBox getChkUseSpecifiedFactionToGenerateForceIcons() { } public void setChkUseSpecifiedFactionToGenerateForceIcons( - final JCheckBox chkUseSpecifiedFactionToGenerateForceIcons) { + final JCheckBox chkUseSpecifiedFactionToGenerateForceIcons) { this.chkUseSpecifiedFactionToGenerateForceIcons = chkUseSpecifiedFactionToGenerateForceIcons; } @@ -637,7 +636,7 @@ public JCheckBox getChkGenerateFractionalMachineGunAmmunition() { } public void setChkGenerateFractionalMachineGunAmmunition( - final JCheckBox chkGenerateFractionalMachineGunAmmunition) { + final JCheckBox chkGenerateFractionalMachineGunAmmunition) { this.chkGenerateFractionalMachineGunAmmunition = chkGenerateFractionalMachineGunAmmunition; } // endregion Spares @@ -795,9 +794,9 @@ public void setChkGenerateMysteryBoxTypes(final Map c // region Determination Methods public int determineMaximumSupportPersonnel() { - return ((getChkGenerateMercenaryCompanyCommandLance().isSelected() ? 1 : 0) - + ((int) getSpnCompanyCount().getValue() * (int) getSpnLancesPerCompany().getValue()) - + (int) getSpnIndividualLanceCount().getValue()) * (int) getSpnLanceSize().getValue(); + return ((getChkGenerateMercenaryCompanyCommandLance().isSelected() ? 1 : 0) + + ((int) getSpnCompanyCount().getValue() * (int) getSpnLancesPerCompany().getValue()) + + (int) getSpnIndividualLanceCount().getValue()) * (int) getSpnLanceSize().getValue(); } // endregion Determination Methods @@ -849,14 +848,13 @@ private JPanel createBaseInformationPanel() { lblCompanyGenerationMethod.setToolTipText(resources.getString("lblCompanyGenerationMethod.toolTipText")); lblCompanyGenerationMethod.setName("lblCompanyGenerationMethod"); - setComboCompanyGenerationMethod( - new MMComboBox<>("comboCompanyGenerationMethod", CompanyGenerationMethod.values())); + setComboCompanyGenerationMethod(new MMComboBox<>("comboCompanyGenerationMethod", + CompanyGenerationMethod.values())); getComboCompanyGenerationMethod().setToolTipText(resources.getString("lblCompanyGenerationMethod.toolTipText")); getComboCompanyGenerationMethod().setRenderer(new DefaultListCellRenderer() { @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { + public Component getListCellRendererComponent(final JList list, final Object value, final int index, + final boolean isSelected, final boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof CompanyGenerationMethod) { list.setToolTipText(((CompanyGenerationMethod) value).getToolTipText()); @@ -870,16 +868,16 @@ public Component getListCellRendererComponent(final JList list, final Object lblSpecifiedFaction.setName("lblSpecifiedFaction"); final DefaultComboBoxModel specifiedFactionModel = new DefaultComboBoxModel<>(); - specifiedFactionModel.addAll(FactionDisplay - .getSortedValidFactionDisplays(Factions.getInstance().getChoosableFactions(), - getCampaign().getLocalDate())); + specifiedFactionModel.addAll(FactionDisplay.getSortedValidFactionDisplays(Factions.getInstance() + .getChoosableFactions(), + getCampaign().getLocalDate())); setComboSpecifiedFaction(new MMComboBox<>("comboFaction", specifiedFactionModel)); getComboSpecifiedFaction().setToolTipText(resources.getString("lblSpecifiedFaction.toolTipText")); - setChkGenerateMercenaryCompanyCommandLance( - new JCheckBox(resources.getString("chkGenerateMercenaryCompanyCommandLance.text"))); - getChkGenerateMercenaryCompanyCommandLance() - .setToolTipText(resources.getString("chkGenerateMercenaryCompanyCommandLance.toolTipText")); + setChkGenerateMercenaryCompanyCommandLance(new JCheckBox(resources.getString( + "chkGenerateMercenaryCompanyCommandLance.text"))); + getChkGenerateMercenaryCompanyCommandLance().setToolTipText(resources.getString( + "chkGenerateMercenaryCompanyCommandLance.toolTipText")); getChkGenerateMercenaryCompanyCommandLance().setName("chkGenerateMercenaryCompanyCommandLance"); final JLabel lblCompanyCount = new JLabel(resources.getString("lblCompanyCount.text")); @@ -942,51 +940,50 @@ public Component getListCellRendererComponent(final JList list, final Object layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblCompanyGenerationMethod) - .addComponent(getComboCompanyGenerationMethod(), Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblSpecifiedFaction) - .addComponent(getComboSpecifiedFaction(), Alignment.LEADING)) - .addComponent(getChkGenerateMercenaryCompanyCommandLance()) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblCompanyCount) - .addComponent(getSpnCompanyCount()) - .addComponent(lblIndividualLanceCount) - .addComponent(getSpnIndividualLanceCount(), Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblLancesPerCompany) - .addComponent(getSpnLancesPerCompany()) - .addComponent(lblLanceSize) - .addComponent(getSpnLanceSize(), Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblStarLeagueYear) - .addComponent(getSpnStarLeagueYear(), Alignment.LEADING))); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblCompanyGenerationMethod) - .addComponent(getComboCompanyGenerationMethod())) - .addGroup(layout.createSequentialGroup() - .addComponent(lblSpecifiedFaction) - .addComponent(getComboSpecifiedFaction())) - .addComponent(getChkGenerateMercenaryCompanyCommandLance()) - .addGroup(layout.createSequentialGroup() - .addComponent(lblCompanyCount) - .addComponent(getSpnCompanyCount()) - .addComponent(lblIndividualLanceCount) - .addComponent(getSpnIndividualLanceCount())) - .addGroup(layout.createSequentialGroup() - .addComponent(lblLancesPerCompany) - .addComponent(getSpnLancesPerCompany()) - .addComponent(lblLanceSize) - .addComponent(getSpnLanceSize())) - .addGroup(layout.createSequentialGroup() - .addComponent(lblStarLeagueYear) - .addComponent(getSpnStarLeagueYear()))); + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblCompanyGenerationMethod) + .addComponent(getComboCompanyGenerationMethod(), + Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblSpecifiedFaction) + .addComponent(getComboSpecifiedFaction(), Alignment.LEADING)) + .addComponent(getChkGenerateMercenaryCompanyCommandLance()) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblCompanyCount) + .addComponent(getSpnCompanyCount()) + .addComponent(lblIndividualLanceCount) + .addComponent(getSpnIndividualLanceCount(), Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblLancesPerCompany) + .addComponent(getSpnLancesPerCompany()) + .addComponent(lblLanceSize) + .addComponent(getSpnLanceSize(), Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblStarLeagueYear) + .addComponent(getSpnStarLeagueYear(), Alignment.LEADING))); + + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(lblCompanyGenerationMethod) + .addComponent(getComboCompanyGenerationMethod())) + .addGroup(layout.createSequentialGroup() + .addComponent(lblSpecifiedFaction) + .addComponent(getComboSpecifiedFaction())) + .addComponent(getChkGenerateMercenaryCompanyCommandLance()) + .addGroup(layout.createSequentialGroup() + .addComponent(lblCompanyCount) + .addComponent(getSpnCompanyCount()) + .addComponent(lblIndividualLanceCount) + .addComponent(getSpnIndividualLanceCount())) + .addGroup(layout.createSequentialGroup() + .addComponent(lblLancesPerCompany) + .addComponent(getSpnLancesPerCompany()) + .addComponent(lblLanceSize) + .addComponent(getSpnLanceSize())) + .addGroup(layout.createSequentialGroup() + .addComponent(lblStarLeagueYear) + .addComponent(getSpnStarLeagueYear()))); return panel; } @@ -1009,60 +1006,59 @@ private JPanel createPersonnelPanel() { getChkGenerateCaptains().setName("chkGenerateCaptains"); setChkAssignCompanyCommanderFlag(new JCheckBox(resources.getString("chkAssignCompanyCommanderFlag.text"))); - getChkAssignCompanyCommanderFlag() - .setToolTipText(resources.getString("chkAssignCompanyCommanderFlag.toolTipText")); + getChkAssignCompanyCommanderFlag().setToolTipText(resources.getString( + "chkAssignCompanyCommanderFlag.toolTipText")); getChkAssignCompanyCommanderFlag().setName("chkAssignCompanyCommanderFlag"); - setChkApplyOfficerStatBonusToWorstSkill( - new JCheckBox(resources.getString("chkApplyOfficerStatBonusToWorstSkill.text"))); - getChkApplyOfficerStatBonusToWorstSkill() - .setToolTipText(resources.getString("chkApplyOfficerStatBonusToWorstSkill.toolTipText")); + setChkApplyOfficerStatBonusToWorstSkill(new JCheckBox(resources.getString( + "chkApplyOfficerStatBonusToWorstSkill.text"))); + getChkApplyOfficerStatBonusToWorstSkill().setToolTipText(resources.getString( + "chkApplyOfficerStatBonusToWorstSkill.toolTipText")); getChkApplyOfficerStatBonusToWorstSkill().setName("chkApplyOfficerStatBonusToWorstSkill"); setChkAssignBestCompanyCommander(new JCheckBox(resources.getString("chkAssignBestCompanyCommander.text"))); - getChkAssignBestCompanyCommander() - .setToolTipText(resources.getString("chkAssignBestCompanyCommander.toolTipText")); + getChkAssignBestCompanyCommander().setToolTipText(resources.getString( + "chkAssignBestCompanyCommander.toolTipText")); getChkAssignBestCompanyCommander().setName("chkAssignBestCompanyCommander"); - getChkAssignBestCompanyCommander().addActionListener(evt -> getChkPrioritizeCompanyCommanderCombatSkills() - .setEnabled(getChkAssignBestCompanyCommander().isSelected())); + getChkAssignBestCompanyCommander().addActionListener(evt -> getChkPrioritizeCompanyCommanderCombatSkills().setEnabled( + getChkAssignBestCompanyCommander().isSelected())); - setChkPrioritizeCompanyCommanderCombatSkills( - new JCheckBox(resources.getString("chkPrioritizeCompanyCommanderCombatSkills.text"))); - getChkPrioritizeCompanyCommanderCombatSkills() - .setToolTipText(resources.getString("chkPrioritizeCompanyCommanderCombatSkills.toolTipText")); + setChkPrioritizeCompanyCommanderCombatSkills(new JCheckBox(resources.getString( + "chkPrioritizeCompanyCommanderCombatSkills.text"))); + getChkPrioritizeCompanyCommanderCombatSkills().setToolTipText(resources.getString( + "chkPrioritizeCompanyCommanderCombatSkills.toolTipText")); getChkPrioritizeCompanyCommanderCombatSkills().setName("chkPrioritizeCompanyCommanderCombatSkills"); setChkAssignBestOfficers(new JCheckBox(resources.getString("chkAssignBestOfficers.text"))); getChkAssignBestOfficers().setToolTipText(resources.getString("chkAssignBestOfficers.toolTipText")); getChkAssignBestOfficers().setName("chkAssignBestOfficers"); - getChkAssignBestOfficers().addActionListener( - evt -> getChkPrioritizeOfficerCombatSkills().setEnabled(getChkAssignBestOfficers().isSelected())); + getChkAssignBestOfficers().addActionListener(evt -> getChkPrioritizeOfficerCombatSkills().setEnabled( + getChkAssignBestOfficers().isSelected())); - setChkPrioritizeOfficerCombatSkills( - new JCheckBox(resources.getString("chkPrioritizeOfficerCombatSkills.text"))); - getChkPrioritizeOfficerCombatSkills() - .setToolTipText(resources.getString("chkPrioritizeOfficerCombatSkills.toolTipText")); + setChkPrioritizeOfficerCombatSkills(new JCheckBox(resources.getString("chkPrioritizeOfficerCombatSkills.text"))); + getChkPrioritizeOfficerCombatSkills().setToolTipText(resources.getString( + "chkPrioritizeOfficerCombatSkills.toolTipText")); getChkPrioritizeOfficerCombatSkills().setName("chkPrioritizeOfficerCombatSkills"); - setChkAssignMostSkilledToPrimaryLances( - new JCheckBox(resources.getString("chkAssignMostSkilledToPrimaryLances.text"))); - getChkAssignMostSkilledToPrimaryLances() - .setToolTipText(resources.getString("chkAssignMostSkilledToPrimaryLances.toolTipText")); + setChkAssignMostSkilledToPrimaryLances(new JCheckBox(resources.getString( + "chkAssignMostSkilledToPrimaryLances.text"))); + getChkAssignMostSkilledToPrimaryLances().setToolTipText(resources.getString( + "chkAssignMostSkilledToPrimaryLances.toolTipText")); getChkAssignMostSkilledToPrimaryLances().setName("chkAssignMostSkilledToPrimaryLances"); setChkAutomaticallyAssignRanks(new JCheckBox(resources.getString("chkAutomaticallyAssignRanks.text"))); getChkAutomaticallyAssignRanks().setToolTipText(resources.getString("chkAutomaticallyAssignRanks.toolTipText")); getChkAutomaticallyAssignRanks().setName("chkAutomaticallyAssignRanks"); - setChkUseSpecifiedFactionToAssignRanks( - new JCheckBox(resources.getString("chkUseSpecifiedFactionToAssignRanks.text"))); - getChkUseSpecifiedFactionToAssignRanks() - .setToolTipText(resources.getString("chkUseSpecifiedFactionToAssignRanks.toolTipText")); + setChkUseSpecifiedFactionToAssignRanks(new JCheckBox(resources.getString( + "chkUseSpecifiedFactionToAssignRanks.text"))); + getChkUseSpecifiedFactionToAssignRanks().setToolTipText(resources.getString( + "chkUseSpecifiedFactionToAssignRanks.toolTipText")); getChkUseSpecifiedFactionToAssignRanks().setName("chkUseSpecifiedFactionToAssignRanks"); setChkAssignMekWarriorsCallsigns(new JCheckBox(resources.getString("chkAssignMekWarriorsCallsigns.text"))); - getChkAssignMekWarriorsCallsigns() - .setToolTipText(resources.getString("chkAssignMekWarriorsCallsigns.toolTipText")); + getChkAssignMekWarriorsCallsigns().setToolTipText(resources.getString( + "chkAssignMekWarriorsCallsigns.toolTipText")); getChkAssignMekWarriorsCallsigns().setName("chkAssignMekWarriorsCallsigns"); setChkAssignFounderFlag(new JCheckBox(resources.getString("chkAssignFounderFlag.text"))); @@ -1085,58 +1081,57 @@ private JPanel createPersonnelPanel() { layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(getLblTotalSupportPersonnel()) - .addComponent(supportPersonnelNumbersPanel) - .addComponent(getChkPoolAssistants()) - .addComponent(getChkGenerateCaptains()) - .addComponent(getChkAssignCompanyCommanderFlag()) - .addComponent(getChkApplyOfficerStatBonusToWorstSkill()) - .addComponent(getChkAssignBestCompanyCommander()) - .addComponent(getChkPrioritizeCompanyCommanderCombatSkills()) - .addComponent(getChkAssignBestOfficers()) - .addComponent(getChkPrioritizeOfficerCombatSkills()) - .addComponent(getChkAssignMostSkilledToPrimaryLances()) - .addComponent(getChkAutomaticallyAssignRanks()) - .addComponent(getChkUseSpecifiedFactionToAssignRanks()) - .addComponent(getChkAssignMekWarriorsCallsigns()) - .addComponent(getChkAssignFounderFlag())); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(getLblTotalSupportPersonnel()) - .addComponent(supportPersonnelNumbersPanel) - .addComponent(getChkPoolAssistants()) - .addComponent(getChkGenerateCaptains()) - .addComponent(getChkAssignCompanyCommanderFlag()) - .addComponent(getChkApplyOfficerStatBonusToWorstSkill()) - .addComponent(getChkAssignBestCompanyCommander()) - .addComponent(getChkPrioritizeCompanyCommanderCombatSkills()) - .addComponent(getChkAssignBestOfficers()) - .addComponent(getChkPrioritizeOfficerCombatSkills()) - .addComponent(getChkAssignMostSkilledToPrimaryLances()) - .addComponent(getChkAutomaticallyAssignRanks()) - .addComponent(getChkUseSpecifiedFactionToAssignRanks()) - .addComponent(getChkAssignMekWarriorsCallsigns()) - .addComponent(getChkAssignFounderFlag())); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(getLblTotalSupportPersonnel()) + .addComponent(supportPersonnelNumbersPanel) + .addComponent(getChkPoolAssistants()) + .addComponent(getChkGenerateCaptains()) + .addComponent(getChkAssignCompanyCommanderFlag()) + .addComponent(getChkApplyOfficerStatBonusToWorstSkill()) + .addComponent(getChkAssignBestCompanyCommander()) + .addComponent(getChkPrioritizeCompanyCommanderCombatSkills()) + .addComponent(getChkAssignBestOfficers()) + .addComponent(getChkPrioritizeOfficerCombatSkills()) + .addComponent(getChkAssignMostSkilledToPrimaryLances()) + .addComponent(getChkAutomaticallyAssignRanks()) + .addComponent(getChkUseSpecifiedFactionToAssignRanks()) + .addComponent(getChkAssignMekWarriorsCallsigns()) + .addComponent(getChkAssignFounderFlag())); + + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(getLblTotalSupportPersonnel()) + .addComponent(supportPersonnelNumbersPanel) + .addComponent(getChkPoolAssistants()) + .addComponent(getChkGenerateCaptains()) + .addComponent(getChkAssignCompanyCommanderFlag()) + .addComponent(getChkApplyOfficerStatBonusToWorstSkill()) + .addComponent(getChkAssignBestCompanyCommander()) + .addComponent(getChkPrioritizeCompanyCommanderCombatSkills()) + .addComponent(getChkAssignBestOfficers()) + .addComponent(getChkPrioritizeOfficerCombatSkills()) + .addComponent(getChkAssignMostSkilledToPrimaryLances()) + .addComponent(getChkAutomaticallyAssignRanks()) + .addComponent(getChkUseSpecifiedFactionToAssignRanks()) + .addComponent(getChkAssignMekWarriorsCallsigns()) + .addComponent(getChkAssignFounderFlag())); return panel; } private JPanel createSupportPersonnelNumbersPanel() { - final PersonnelRole[] personnelRoles = new PersonnelRole[] { - PersonnelRole.MEK_TECH, PersonnelRole.MECHANIC, PersonnelRole.AERO_TEK, - PersonnelRole.BA_TECH, PersonnelRole.DOCTOR, PersonnelRole.ADMINISTRATOR_COMMAND, - PersonnelRole.ADMINISTRATOR_LOGISTICS, PersonnelRole.ADMINISTRATOR_TRANSPORT, - PersonnelRole.ADMINISTRATOR_HR - }; + final PersonnelRole[] personnelRoles = new PersonnelRole[] { PersonnelRole.MEK_TECH, PersonnelRole.MECHANIC, + PersonnelRole.AERO_TEK, PersonnelRole.BA_TECH, + PersonnelRole.DOCTOR, + PersonnelRole.ADMINISTRATOR_COMMAND, + PersonnelRole.ADMINISTRATOR_LOGISTICS, + PersonnelRole.ADMINISTRATOR_TRANSPORT, + PersonnelRole.ADMINISTRATOR_HR }; // Create Panel Components setSpnSupportPersonnelNumbers(new HashMap<>()); final Map labels = new HashMap<>(); for (final PersonnelRole role : personnelRoles) { - final String name = role.getName(getCampaign().getFaction().isClan()); + final String name = role.getLabel(getCampaign().getFaction().isClan()); final String toolTipText = String.format(resources.getString("supportPersonnelNumber.toolTipText"), name); labels.put(role, new JLabel(name)); @@ -1172,8 +1167,9 @@ private JPanel createSupportPersonnelNumbersPanel() { } private JPanel createPersonnelRandomizationPanel() { - setRandomOriginOptionsPanel(new RandomOriginOptionsPanel(getFrame(), getCampaign(), - getCampaign().getFaction())); + setRandomOriginOptionsPanel(new RandomOriginOptionsPanel(getFrame(), + getCampaign(), + getCampaign().getFaction())); return getRandomOriginOptionsPanel(); } @@ -1206,8 +1202,7 @@ private JPanel createStartingSimulationPanel() { getChkSimulateRandomMarriages().setName("chkSimulateRandomMarriages"); setChkSimulateRandomProcreation(new JCheckBox(resources.getString("chkSimulateRandomProcreation.text"))); - getChkSimulateRandomProcreation() - .setToolTipText(resources.getString("chkSimulateRandomProcreation.toolTipText")); + getChkSimulateRandomProcreation().setToolTipText(resources.getString("chkSimulateRandomProcreation.toolTipText")); getChkSimulateRandomProcreation().setName("chkSimulateRandomProcreation"); // Programmatically Assign Accessibility Labels @@ -1227,44 +1222,41 @@ private JPanel createStartingSimulationPanel() { layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(getChkRunStartingSimulation()) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblSimulationDuration) - .addComponent(getSpnSimulationDuration(), Alignment.LEADING)) - .addComponent(getChkSimulateRandomMarriages()) - .addComponent(getChkSimulateRandomProcreation())); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(getChkRunStartingSimulation()) - .addGroup(layout.createSequentialGroup() - .addComponent(lblSimulationDuration) - .addComponent(getSpnSimulationDuration())) - .addComponent(getChkSimulateRandomMarriages()) - .addComponent(getChkSimulateRandomProcreation())); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(getChkRunStartingSimulation()) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblSimulationDuration) + .addComponent(getSpnSimulationDuration(), Alignment.LEADING)) + .addComponent(getChkSimulateRandomMarriages()) + .addComponent(getChkSimulateRandomProcreation())); + + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(getChkRunStartingSimulation()) + .addGroup(layout.createSequentialGroup() + .addComponent(lblSimulationDuration) + .addComponent(getSpnSimulationDuration())) + .addComponent(getChkSimulateRandomMarriages()) + .addComponent(getChkSimulateRandomProcreation())); return panel; } private JPanel createUnitsPanel() { // Create Panel Components - final JLabel lblBattleMekFactionGenerationMethod = new JLabel( - resources.getString("lblBattleMekFactionGenerationMethod.text")); - lblBattleMekFactionGenerationMethod - .setToolTipText(resources.getString("lblBattleMekFactionGenerationMethod.toolTipText")); + final JLabel lblBattleMekFactionGenerationMethod = new JLabel(resources.getString( + "lblBattleMekFactionGenerationMethod.text")); + lblBattleMekFactionGenerationMethod.setToolTipText(resources.getString( + "lblBattleMekFactionGenerationMethod.toolTipText")); lblBattleMekFactionGenerationMethod.setName("lblBattleMekFactionGenerationMethod"); - setComboBattleMekFactionGenerationMethod( - new MMComboBox<>("comboBattleMekFactionGenerationMethod", BattleMekFactionGenerationMethod.values())); - getComboBattleMekFactionGenerationMethod() - .setToolTipText(resources.getString("lblBattleMekFactionGenerationMethod.toolTipText")); + setComboBattleMekFactionGenerationMethod(new MMComboBox<>("comboBattleMekFactionGenerationMethod", + BattleMekFactionGenerationMethod.values())); + getComboBattleMekFactionGenerationMethod().setToolTipText(resources.getString( + "lblBattleMekFactionGenerationMethod.toolTipText")); getComboBattleMekFactionGenerationMethod().setRenderer(new DefaultListCellRenderer() { @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { + public Component getListCellRendererComponent(final JList list, final Object value, final int index, + final boolean isSelected, final boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof BattleMekFactionGenerationMethod) { list.setToolTipText(((BattleMekFactionGenerationMethod) value).getToolTipText()); @@ -1273,21 +1265,20 @@ public Component getListCellRendererComponent(final JList list, final Object } }); - final JLabel lblBattleMekWeightClassGenerationMethod = new JLabel( - resources.getString("lblBattleMekWeightClassGenerationMethod.text")); - lblBattleMekWeightClassGenerationMethod - .setToolTipText(resources.getString("lblBattleMekWeightClassGenerationMethod.toolTipText")); + final JLabel lblBattleMekWeightClassGenerationMethod = new JLabel(resources.getString( + "lblBattleMekWeightClassGenerationMethod.text")); + lblBattleMekWeightClassGenerationMethod.setToolTipText(resources.getString( + "lblBattleMekWeightClassGenerationMethod.toolTipText")); lblBattleMekWeightClassGenerationMethod.setName("lblBattleMekWeightClassGenerationMethod"); setComboBattleMekWeightClassGenerationMethod(new MMComboBox<>("comboBattleMekWeightClassGenerationMethod", - BattleMekWeightClassGenerationMethod.values())); - getComboBattleMekWeightClassGenerationMethod() - .setToolTipText(resources.getString("lblBattleMekWeightClassGenerationMethod.toolTipText")); + BattleMekWeightClassGenerationMethod.values())); + getComboBattleMekWeightClassGenerationMethod().setToolTipText(resources.getString( + "lblBattleMekWeightClassGenerationMethod.toolTipText")); getComboBattleMekWeightClassGenerationMethod().setRenderer(new DefaultListCellRenderer() { @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { + public Component getListCellRendererComponent(final JList list, final Object value, final int index, + final boolean isSelected, final boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof BattleMekWeightClassGenerationMethod) { list.setToolTipText(((BattleMekWeightClassGenerationMethod) value).getToolTipText()); @@ -1296,21 +1287,20 @@ public Component getListCellRendererComponent(final JList list, final Object } }); - final JLabel lblBattleMekQualityGenerationMethod = new JLabel( - resources.getString("lblBattleMekQualityGenerationMethod.text")); - lblBattleMekQualityGenerationMethod - .setToolTipText(resources.getString("lblBattleMekQualityGenerationMethod.toolTipText")); + final JLabel lblBattleMekQualityGenerationMethod = new JLabel(resources.getString( + "lblBattleMekQualityGenerationMethod.text")); + lblBattleMekQualityGenerationMethod.setToolTipText(resources.getString( + "lblBattleMekQualityGenerationMethod.toolTipText")); lblBattleMekQualityGenerationMethod.setName("lblBattleMekQualityGenerationMethod"); - setComboBattleMekQualityGenerationMethod( - new MMComboBox<>("comboBattleMekQualityGenerationMethod", BattleMekQualityGenerationMethod.values())); - getComboBattleMekQualityGenerationMethod() - .setToolTipText(resources.getString("lblBattleMekQualityGenerationMethod.toolTipText")); + setComboBattleMekQualityGenerationMethod(new MMComboBox<>("comboBattleMekQualityGenerationMethod", + BattleMekQualityGenerationMethod.values())); + getComboBattleMekQualityGenerationMethod().setToolTipText(resources.getString( + "lblBattleMekQualityGenerationMethod.toolTipText")); getComboBattleMekQualityGenerationMethod().setRenderer(new DefaultListCellRenderer() { @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { + public Component getListCellRendererComponent(final JList list, final Object value, final int index, + final boolean isSelected, final boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof BattleMekQualityGenerationMethod) { list.setToolTipText(((BattleMekQualityGenerationMethod) value).getToolTipText()); @@ -1320,18 +1310,16 @@ public Component getListCellRendererComponent(final JList list, final Object }); setChkNeverGenerateStarLeagueMeks(new JCheckBox(resources.getString("chkNeverGenerateStarLeagueMeks.text"))); - getChkNeverGenerateStarLeagueMeks() - .setToolTipText(resources.getString("chkNeverGenerateStarLeagueMeks.toolTipText")); + getChkNeverGenerateStarLeagueMeks().setToolTipText(resources.getString( + "chkNeverGenerateStarLeagueMeks.toolTipText")); getChkNeverGenerateStarLeagueMeks().setName("chkNeverGenerateStarLeagueMeks"); - getChkNeverGenerateStarLeagueMeks().addActionListener(evt -> getChkOnlyGenerateStarLeagueMeks() - .setEnabled(!getChkNeverGenerateStarLeagueMeks().isSelected())); + getChkNeverGenerateStarLeagueMeks().addActionListener(evt -> getChkOnlyGenerateStarLeagueMeks().setEnabled(!getChkNeverGenerateStarLeagueMeks().isSelected())); setChkOnlyGenerateStarLeagueMeks(new JCheckBox(resources.getString("chkOnlyGenerateStarLeagueMeks.text"))); - getChkOnlyGenerateStarLeagueMeks() - .setToolTipText(resources.getString("chkOnlyGenerateStarLeagueMeks.toolTipText")); + getChkOnlyGenerateStarLeagueMeks().setToolTipText(resources.getString( + "chkOnlyGenerateStarLeagueMeks.toolTipText")); getChkOnlyGenerateStarLeagueMeks().setName("chkOnlyGenerateStarLeagueMeks"); - getChkOnlyGenerateStarLeagueMeks().addActionListener(evt -> getChkNeverGenerateStarLeagueMeks() - .setEnabled(!getChkOnlyGenerateStarLeagueMeks().isSelected())); + getChkOnlyGenerateStarLeagueMeks().addActionListener(evt -> getChkNeverGenerateStarLeagueMeks().setEnabled(!getChkOnlyGenerateStarLeagueMeks().isSelected())); setChkOnlyGenerateOmniMeks(new JCheckBox(resources.getString("chkOnlyGenerateOmniMeks.text"))); getChkOnlyGenerateOmniMeks().setToolTipText(resources.getString("chkOnlyGenerateOmniMeks.toolTipText")); @@ -1341,10 +1329,10 @@ public Component getListCellRendererComponent(final JList list, final Object getChkGenerateUnitsAsAttached().setToolTipText(resources.getString("chkGenerateUnitsAsAttached.toolTipText")); getChkGenerateUnitsAsAttached().setName("chkGenerateUnitsAsAttached"); - setChkAssignBestRollToCompanyCommander( - new JCheckBox(resources.getString("chkAssignBestRollToCompanyCommander.text"))); - getChkAssignBestRollToCompanyCommander() - .setToolTipText(resources.getString("chkAssignBestRollToCompanyCommander.toolTipText")); + setChkAssignBestRollToCompanyCommander(new JCheckBox(resources.getString( + "chkAssignBestRollToCompanyCommander.text"))); + getChkAssignBestRollToCompanyCommander().setToolTipText(resources.getString( + "chkAssignBestRollToCompanyCommander.toolTipText")); getChkAssignBestRollToCompanyCommander().setName("chkAssignBestRollToCompanyCommander"); setChkSortStarLeagueUnitsFirst(new JCheckBox(resources.getString("chkSortStarLeagueUnitsFirst.text"))); @@ -1382,49 +1370,50 @@ public Component getListCellRendererComponent(final JList list, final Object layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblBattleMekFactionGenerationMethod) - .addComponent(getComboBattleMekFactionGenerationMethod(), Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblBattleMekWeightClassGenerationMethod) - .addComponent(getComboBattleMekWeightClassGenerationMethod(), Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblBattleMekQualityGenerationMethod) - .addComponent(getComboBattleMekQualityGenerationMethod(), Alignment.LEADING)) - .addComponent(getChkNeverGenerateStarLeagueMeks()) - .addComponent(getChkOnlyGenerateStarLeagueMeks()) - .addComponent(getChkOnlyGenerateOmniMeks()) - .addComponent(getChkGenerateUnitsAsAttached()) - .addComponent(getChkAssignBestRollToCompanyCommander()) - .addComponent(getChkSortStarLeagueUnitsFirst()) - .addComponent(getChkGroupByWeight()) - .addComponent(getChkGroupByQuality()) - .addComponent(getChkKeepOfficerRollsSeparate()) - .addComponent(getChkAssignTechsToUnits())); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblBattleMekFactionGenerationMethod) - .addComponent(getComboBattleMekFactionGenerationMethod())) - .addGroup(layout.createSequentialGroup() - .addComponent(lblBattleMekWeightClassGenerationMethod) - .addComponent(getComboBattleMekWeightClassGenerationMethod())) - .addGroup(layout.createSequentialGroup() - .addComponent(lblBattleMekQualityGenerationMethod) - .addComponent(getComboBattleMekQualityGenerationMethod())) - .addComponent(getChkNeverGenerateStarLeagueMeks()) - .addComponent(getChkOnlyGenerateStarLeagueMeks()) - .addComponent(getChkOnlyGenerateOmniMeks()) - .addComponent(getChkGenerateUnitsAsAttached()) - .addComponent(getChkAssignBestRollToCompanyCommander()) - .addComponent(getChkSortStarLeagueUnitsFirst()) - .addComponent(getChkGroupByWeight()) - .addComponent(getChkGroupByQuality()) - .addComponent(getChkKeepOfficerRollsSeparate()) - .addComponent(getChkAssignTechsToUnits())); + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblBattleMekFactionGenerationMethod) + .addComponent(getComboBattleMekFactionGenerationMethod(), + Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblBattleMekWeightClassGenerationMethod) + .addComponent(getComboBattleMekWeightClassGenerationMethod(), + Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblBattleMekQualityGenerationMethod) + .addComponent(getComboBattleMekQualityGenerationMethod(), + Alignment.LEADING)) + .addComponent(getChkNeverGenerateStarLeagueMeks()) + .addComponent(getChkOnlyGenerateStarLeagueMeks()) + .addComponent(getChkOnlyGenerateOmniMeks()) + .addComponent(getChkGenerateUnitsAsAttached()) + .addComponent(getChkAssignBestRollToCompanyCommander()) + .addComponent(getChkSortStarLeagueUnitsFirst()) + .addComponent(getChkGroupByWeight()) + .addComponent(getChkGroupByQuality()) + .addComponent(getChkKeepOfficerRollsSeparate()) + .addComponent(getChkAssignTechsToUnits())); + + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(lblBattleMekFactionGenerationMethod) + .addComponent(getComboBattleMekFactionGenerationMethod())) + .addGroup(layout.createSequentialGroup() + .addComponent(lblBattleMekWeightClassGenerationMethod) + .addComponent(getComboBattleMekWeightClassGenerationMethod())) + .addGroup(layout.createSequentialGroup() + .addComponent(lblBattleMekQualityGenerationMethod) + .addComponent(getComboBattleMekQualityGenerationMethod())) + .addComponent(getChkNeverGenerateStarLeagueMeks()) + .addComponent(getChkOnlyGenerateStarLeagueMeks()) + .addComponent(getChkOnlyGenerateOmniMeks()) + .addComponent(getChkGenerateUnitsAsAttached()) + .addComponent(getChkAssignBestRollToCompanyCommander()) + .addComponent(getChkSortStarLeagueUnitsFirst()) + .addComponent(getChkGroupByWeight()) + .addComponent(getChkGroupByQuality()) + .addComponent(getChkKeepOfficerRollsSeparate()) + .addComponent(getChkAssignTechsToUnits())); return panel; } @@ -1442,9 +1431,8 @@ private JPanel createUnitPanel() { getComboForceNamingMethod().setToolTipText(resources.getString("lblForceNamingMethod.toolTipText")); getComboForceNamingMethod().setRenderer(new DefaultListCellRenderer() { @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { + public Component getListCellRendererComponent(final JList list, final Object value, final int index, + final boolean isSelected, final boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof ForceNamingMethod) { list.setToolTipText(((ForceNamingMethod) value).getToolTipText()); @@ -1460,28 +1448,26 @@ public Component getListCellRendererComponent(final JList list, final Object final boolean selected = getChkGenerateForceIcons().isSelected(); getChkUseSpecifiedFactionToGenerateForceIcons().setEnabled(selected); getChkGenerateOriginNodeForceIcon().setEnabled(selected); - getChkUseOriginNodeForceIconLogo().setEnabled(selected - && getChkGenerateOriginNodeForceIcon().isSelected()); + getChkUseOriginNodeForceIconLogo().setEnabled(selected && getChkGenerateOriginNodeForceIcon().isSelected()); forceWeightLimitsPanel.setEnabled(selected); }); - setChkUseSpecifiedFactionToGenerateForceIcons( - new JCheckBox(resources.getString("chkUseSpecifiedFactionToGenerateForceIcons.text"))); - getChkUseSpecifiedFactionToGenerateForceIcons() - .setToolTipText(resources.getString("chkUseSpecifiedFactionToGenerateForceIcons.toolTipText")); + setChkUseSpecifiedFactionToGenerateForceIcons(new JCheckBox(resources.getString( + "chkUseSpecifiedFactionToGenerateForceIcons.text"))); + getChkUseSpecifiedFactionToGenerateForceIcons().setToolTipText(resources.getString( + "chkUseSpecifiedFactionToGenerateForceIcons.toolTipText")); getChkUseSpecifiedFactionToGenerateForceIcons().setName("chkUseSpecifiedFactionToGenerateForceIcons"); setChkGenerateOriginNodeForceIcon(new JCheckBox(resources.getString("chkGenerateOriginNodeForceIcon.text"))); - getChkGenerateOriginNodeForceIcon() - .setToolTipText(resources.getString("chkGenerateOriginNodeForceIcon.toolTipText")); + getChkGenerateOriginNodeForceIcon().setToolTipText(resources.getString( + "chkGenerateOriginNodeForceIcon.toolTipText")); getChkGenerateOriginNodeForceIcon().setName("chkGenerateOriginNodeForceIcon"); - getChkGenerateOriginNodeForceIcon().addActionListener(evt -> getChkUseOriginNodeForceIconLogo() - .setEnabled(getChkGenerateOriginNodeForceIcon().isEnabled() - && getChkGenerateOriginNodeForceIcon().isSelected())); + getChkGenerateOriginNodeForceIcon().addActionListener(evt -> getChkUseOriginNodeForceIconLogo().setEnabled( + getChkGenerateOriginNodeForceIcon().isEnabled() && getChkGenerateOriginNodeForceIcon().isSelected())); setChkUseOriginNodeForceIconLogo(new JCheckBox(resources.getString("chkUseOriginNodeForceIconLogo.text"))); - getChkUseOriginNodeForceIconLogo() - .setToolTipText(resources.getString("chkUseOriginNodeForceIconLogo.toolTipText")); + getChkUseOriginNodeForceIconLogo().setToolTipText(resources.getString( + "chkUseOriginNodeForceIconLogo.toolTipText")); getChkUseOriginNodeForceIconLogo().setName("chkUseOriginNodeForceIconLogo"); createForceWeightLimitsPanel(forceWeightLimitsPanel); @@ -1499,27 +1485,25 @@ public Component getListCellRendererComponent(final JList list, final Object layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblForceNamingMethod) - .addComponent(getComboForceNamingMethod(), Alignment.LEADING)) - .addComponent(getChkGenerateForceIcons()) - .addComponent(getChkUseSpecifiedFactionToGenerateForceIcons()) - .addComponent(getChkGenerateOriginNodeForceIcon()) - .addComponent(getChkUseOriginNodeForceIconLogo()) - .addComponent(forceWeightLimitsPanel)); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblForceNamingMethod) - .addComponent(getComboForceNamingMethod())) - .addComponent(getChkGenerateForceIcons()) - .addComponent(getChkUseSpecifiedFactionToGenerateForceIcons()) - .addComponent(getChkGenerateOriginNodeForceIcon()) - .addComponent(getChkUseOriginNodeForceIconLogo()) - .addComponent(forceWeightLimitsPanel)); + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblForceNamingMethod) + .addComponent(getComboForceNamingMethod(), Alignment.LEADING)) + .addComponent(getChkGenerateForceIcons()) + .addComponent(getChkUseSpecifiedFactionToGenerateForceIcons()) + .addComponent(getChkGenerateOriginNodeForceIcon()) + .addComponent(getChkUseOriginNodeForceIconLogo()) + .addComponent(forceWeightLimitsPanel)); + + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(lblForceNamingMethod) + .addComponent(getComboForceNamingMethod())) + .addComponent(getChkGenerateForceIcons()) + .addComponent(getChkUseSpecifiedFactionToGenerateForceIcons()) + .addComponent(getChkGenerateOriginNodeForceIcon()) + .addComponent(getChkUseOriginNodeForceIconLogo()) + .addComponent(forceWeightLimitsPanel)); return panel; } @@ -1553,8 +1537,8 @@ private JPanel createSparesPanel() { // Create Panel Components setChkGenerateMothballedSpareUnits(new JCheckBox(resources.getString("chkGenerateMothballedSpareUnits.text"))); - getChkGenerateMothballedSpareUnits() - .setToolTipText(resources.getString("chkGenerateMothballedSpareUnits.toolTipText")); + getChkGenerateMothballedSpareUnits().setToolTipText(resources.getString( + "chkGenerateMothballedSpareUnits.toolTipText")); getChkGenerateMothballedSpareUnits().setName("chkGenerateMothballedSpareUnits"); getChkGenerateMothballedSpareUnits().addActionListener(evt -> { final boolean selected = getChkGenerateMothballedSpareUnits().isSelected(); @@ -1567,8 +1551,8 @@ private JPanel createSparesPanel() { lblSparesPercentOfActiveUnits.setName("lblSparesPercentOfActiveUnits"); setSpnSparesPercentOfActiveUnits(new JSpinner(new SpinnerNumberModel(0, 0, 100, 1))); - getSpnSparesPercentOfActiveUnits() - .setToolTipText(resources.getString("chkGenerateMothballedSpareUnits.toolTipText")); + getSpnSparesPercentOfActiveUnits().setToolTipText(resources.getString( + "chkGenerateMothballedSpareUnits.toolTipText")); getSpnSparesPercentOfActiveUnits().setName("spnGenerateMothballedSpareUnits"); final JLabel lblPartGenerationMethod = new JLabel(resources.getString("lblPartGenerationMethod.text")); @@ -1579,9 +1563,8 @@ private JPanel createSparesPanel() { getComboPartGenerationMethod().setToolTipText(resources.getString("lblPartGenerationMethod.toolTipText")); getComboPartGenerationMethod().setRenderer(new DefaultListCellRenderer() { @Override - public Component getListCellRendererComponent(final JList list, final Object value, - final int index, final boolean isSelected, - final boolean cellHasFocus) { + public Component getListCellRendererComponent(final JList list, final Object value, final int index, + final boolean isSelected, final boolean cellHasFocus) { super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if (value instanceof PartGenerationMethod) { list.setToolTipText(((PartGenerationMethod) value).getToolTipText()); @@ -1616,10 +1599,10 @@ public Component getListCellRendererComponent(final JList list, final Object getSpnNumberReloadsPerWeapon().setToolTipText(resources.getString("lblNumberReloadsPerWeapon.toolTipText")); getSpnNumberReloadsPerWeapon().setName("spnNumberReloadsPerWeapon"); - setChkGenerateFractionalMachineGunAmmunition( - new JCheckBox(resources.getString("chkGenerateFractionalMachineGunAmmunition.text"))); - getChkGenerateFractionalMachineGunAmmunition() - .setToolTipText(resources.getString("chkGenerateFractionalMachineGunAmmunition.toolTipText")); + setChkGenerateFractionalMachineGunAmmunition(new JCheckBox(resources.getString( + "chkGenerateFractionalMachineGunAmmunition.text"))); + getChkGenerateFractionalMachineGunAmmunition().setToolTipText(resources.getString( + "chkGenerateFractionalMachineGunAmmunition.toolTipText")); getChkGenerateFractionalMachineGunAmmunition().setName("chkGenerateFractionalMachineGunAmmunition"); // Programmatically Assign Accessibility Labels @@ -1644,41 +1627,40 @@ public Component getListCellRendererComponent(final JList list, final Object layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(getChkGenerateMothballedSpareUnits()) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblSparesPercentOfActiveUnits) - .addComponent(getSpnSparesPercentOfActiveUnits(), Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPartGenerationMethod) - .addComponent(getComboPartGenerationMethod(), Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblStartingArmourWeight) - .addComponent(getSpnStartingArmourWeight(), Alignment.LEADING)) - .addComponent(getChkGenerateSpareAmmunition()) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblNumberReloadsPerWeapon) - .addComponent(getSpnNumberReloadsPerWeapon(), Alignment.LEADING)) - .addComponent(getChkGenerateFractionalMachineGunAmmunition())); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(getChkGenerateMothballedSpareUnits()) - .addGroup(layout.createSequentialGroup() - .addComponent(lblSparesPercentOfActiveUnits) - .addComponent(getSpnSparesPercentOfActiveUnits())) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPartGenerationMethod) - .addComponent(getComboPartGenerationMethod())) - .addGroup(layout.createSequentialGroup() - .addComponent(lblStartingArmourWeight) - .addComponent(getSpnStartingArmourWeight())) - .addComponent(getChkGenerateSpareAmmunition()) - .addGroup(layout.createSequentialGroup() - .addComponent(lblNumberReloadsPerWeapon) - .addComponent(getSpnNumberReloadsPerWeapon())) - .addComponent(getChkGenerateFractionalMachineGunAmmunition())); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(getChkGenerateMothballedSpareUnits()) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblSparesPercentOfActiveUnits) + .addComponent(getSpnSparesPercentOfActiveUnits(), + Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblPartGenerationMethod) + .addComponent(getComboPartGenerationMethod(), Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblStartingArmourWeight) + .addComponent(getSpnStartingArmourWeight(), Alignment.LEADING)) + .addComponent(getChkGenerateSpareAmmunition()) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblNumberReloadsPerWeapon) + .addComponent(getSpnNumberReloadsPerWeapon(), Alignment.LEADING)) + .addComponent(getChkGenerateFractionalMachineGunAmmunition())); + + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(getChkGenerateMothballedSpareUnits()) + .addGroup(layout.createSequentialGroup() + .addComponent(lblSparesPercentOfActiveUnits) + .addComponent(getSpnSparesPercentOfActiveUnits())) + .addGroup(layout.createSequentialGroup() + .addComponent(lblPartGenerationMethod) + .addComponent(getComboPartGenerationMethod())) + .addGroup(layout.createSequentialGroup() + .addComponent(lblStartingArmourWeight) + .addComponent(getSpnStartingArmourWeight())) + .addComponent(getChkGenerateSpareAmmunition()) + .addGroup(layout.createSequentialGroup() + .addComponent(lblNumberReloadsPerWeapon) + .addComponent(getSpnNumberReloadsPerWeapon())) + .addComponent(getChkGenerateFractionalMachineGunAmmunition())); return panel; } @@ -1697,8 +1679,8 @@ private JPanel createContractsPanel() { }); setChkStartCourseToContractPlanet(new JCheckBox(resources.getString("chkStartCourseToContractPlanet.text"))); - getChkStartCourseToContractPlanet() - .setToolTipText(resources.getString("chkStartCourseToContractPlanet.toolTipText")); + getChkStartCourseToContractPlanet().setToolTipText(resources.getString( + "chkStartCourseToContractPlanet.toolTipText")); getChkStartCourseToContractPlanet().setName("chkStartCourseToContractPlanet"); // Disable Panel by Default @@ -1715,15 +1697,13 @@ private JPanel createContractsPanel() { layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(getChkSelectStartingContract()) - .addComponent(getChkStartCourseToContractPlanet())); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(getChkSelectStartingContract()) + .addComponent(getChkStartCourseToContractPlanet())); - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(getChkSelectStartingContract()) - .addComponent(getChkStartCourseToContractPlanet())); + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(getChkSelectStartingContract()) + .addComponent(getChkStartCourseToContractPlanet())); // TODO : Wave 5 : Company Generation GUI panel.setEnabled(false); @@ -1776,17 +1756,15 @@ private JPanel createFinancesPanel() { layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(getChkProcessFinances()) - .addComponent(financialCreditsPanel) - .addComponent(financialDebitsPanel)); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(getChkProcessFinances()) + .addComponent(financialCreditsPanel) + .addComponent(financialDebitsPanel)); - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(getChkProcessFinances()) - .addComponent(financialCreditsPanel) - .addComponent(financialDebitsPanel)); + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(getChkProcessFinances()) + .addComponent(financialCreditsPanel) + .addComponent(financialDebitsPanel)); return panel; } @@ -1816,13 +1794,12 @@ private void createFinancialCreditsPanel(final JPanel panel) { }); lblRandomStartingCashDiceCount.setText(resources.getString("lblRandomStartingCashDiceCount.text")); - lblRandomStartingCashDiceCount - .setToolTipText(resources.getString("lblRandomStartingCashDiceCount.toolTipText")); + lblRandomStartingCashDiceCount.setToolTipText(resources.getString("lblRandomStartingCashDiceCount.toolTipText")); lblRandomStartingCashDiceCount.setName("lblRandomStartingCashDiceCount"); setSpnRandomStartingCashDiceCount(new JSpinner(new SpinnerNumberModel(8, 1, 100, 1))); - getSpnRandomStartingCashDiceCount() - .setToolTipText(resources.getString("lblRandomStartingCashDiceCount.toolTipText")); + getSpnRandomStartingCashDiceCount().setToolTipText(resources.getString( + "lblRandomStartingCashDiceCount.toolTipText")); getSpnRandomStartingCashDiceCount().setName("spnRandomStartingCashDiceCount"); final JLabel lblMinimumStartingFloat = new JLabel(resources.getString("lblMinimumStartingFloat.text")); @@ -1833,10 +1810,9 @@ private void createFinancialCreditsPanel(final JPanel panel) { getSpnMinimumStartingFloat().setToolTipText(resources.getString("lblMinimumStartingFloat.toolTipText")); getSpnMinimumStartingFloat().setName("spnMinimumStartingFloat"); - setChkIncludeInitialContractPayment( - new JCheckBox(resources.getString("chkIncludeInitialContractPayment.text"))); - getChkIncludeInitialContractPayment() - .setToolTipText(resources.getString("chkIncludeInitialContractPayment.toolTipText")); + setChkIncludeInitialContractPayment(new JCheckBox(resources.getString("chkIncludeInitialContractPayment.text"))); + getChkIncludeInitialContractPayment().setToolTipText(resources.getString( + "chkIncludeInitialContractPayment.toolTipText")); getChkIncludeInitialContractPayment().setName("chkIncludeInitialContractPayment"); setChkStartingLoan(new JCheckBox(resources.getString("chkStartingLoan.text"))); @@ -1859,35 +1835,34 @@ private void createFinancialCreditsPanel(final JPanel panel) { layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblStartingCash) - .addComponent(getSpnStartingCash(), Alignment.LEADING)) - .addComponent(getChkRandomizeStartingCash()) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblRandomStartingCashDiceCount) - .addComponent(getSpnRandomStartingCashDiceCount(), Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblMinimumStartingFloat) - .addComponent(getSpnMinimumStartingFloat(), Alignment.LEADING)) - .addComponent(getChkIncludeInitialContractPayment()) - .addComponent(getChkStartingLoan())); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(lblStartingCash) - .addComponent(getSpnStartingCash())) - .addComponent(getChkRandomizeStartingCash()) - .addGroup(layout.createSequentialGroup() - .addComponent(lblRandomStartingCashDiceCount) - .addComponent(getSpnRandomStartingCashDiceCount())) - .addGroup(layout.createSequentialGroup() - .addComponent(lblMinimumStartingFloat) - .addComponent(getSpnMinimumStartingFloat())) - .addComponent(getChkIncludeInitialContractPayment()) - .addComponent(getChkStartingLoan())); + layout.setVerticalGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblStartingCash) + .addComponent(getSpnStartingCash(), Alignment.LEADING)) + .addComponent(getChkRandomizeStartingCash()) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblRandomStartingCashDiceCount) + .addComponent(getSpnRandomStartingCashDiceCount(), + Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblMinimumStartingFloat) + .addComponent(getSpnMinimumStartingFloat(), Alignment.LEADING)) + .addComponent(getChkIncludeInitialContractPayment()) + .addComponent(getChkStartingLoan())); + + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(lblStartingCash) + .addComponent(getSpnStartingCash())) + .addComponent(getChkRandomizeStartingCash()) + .addGroup(layout.createSequentialGroup() + .addComponent(lblRandomStartingCashDiceCount) + .addComponent(getSpnRandomStartingCashDiceCount())) + .addGroup(layout.createSequentialGroup() + .addComponent(lblMinimumStartingFloat) + .addComponent(getSpnMinimumStartingFloat())) + .addComponent(getChkIncludeInitialContractPayment()) + .addComponent(getChkStartingLoan())); } private void createFinancialDebitsPanel(final JPanel panel) { @@ -1935,23 +1910,21 @@ private void createFinancialDebitsPanel(final JPanel panel) { layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(getChkPayForSetup()) - .addComponent(getChkPayForPersonnel()) - .addComponent(getChkPayForUnits()) - .addComponent(getChkPayForParts()) - .addComponent(getChkPayForArmour()) - .addComponent(getChkPayForAmmunition())); - - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(getChkPayForSetup()) - .addComponent(getChkPayForPersonnel()) - .addComponent(getChkPayForUnits()) - .addComponent(getChkPayForParts()) - .addComponent(getChkPayForArmour()) - .addComponent(getChkPayForAmmunition())); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(getChkPayForSetup()) + .addComponent(getChkPayForPersonnel()) + .addComponent(getChkPayForUnits()) + .addComponent(getChkPayForParts()) + .addComponent(getChkPayForArmour()) + .addComponent(getChkPayForAmmunition())); + + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(getChkPayForSetup()) + .addComponent(getChkPayForPersonnel()) + .addComponent(getChkPayForUnits()) + .addComponent(getChkPayForParts()) + .addComponent(getChkPayForArmour()) + .addComponent(getChkPayForAmmunition())); } private JPanel createSurprisesPanel() { @@ -1971,8 +1944,7 @@ private JPanel createSurprisesPanel() { setChkGenerateMysteryBoxes(new JCheckBox(resources.getString("chkGenerateMysteryBoxes.text"))); getChkGenerateMysteryBoxes().setToolTipText(resources.getString("chkGenerateMysteryBoxes.toolTipText")); getChkGenerateMysteryBoxes().setName("chkGenerateMysteryBoxes"); - getChkGenerateMysteryBoxes().addActionListener(evt -> mysteryBoxPanel.setEnabled( - getChkGenerateMysteryBoxes().isSelected())); + getChkGenerateMysteryBoxes().addActionListener(evt -> mysteryBoxPanel.setEnabled(getChkGenerateMysteryBoxes().isSelected())); createMysteryBoxPanel(mysteryBoxPanel); @@ -1991,17 +1963,15 @@ private JPanel createSurprisesPanel() { layout.setAutoCreateGaps(true); layout.setAutoCreateContainerGaps(true); - layout.setVerticalGroup( - layout.createSequentialGroup() - .addComponent(getChkGenerateSurprises()) - .addComponent(getChkGenerateMysteryBoxes()) - .addComponent(mysteryBoxPanel)); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(getChkGenerateSurprises()) + .addComponent(getChkGenerateMysteryBoxes()) + .addComponent(mysteryBoxPanel)); - layout.setHorizontalGroup( - layout.createParallelGroup(Alignment.LEADING) - .addComponent(getChkGenerateSurprises()) - .addComponent(getChkGenerateMysteryBoxes()) - .addComponent(mysteryBoxPanel)); + layout.setHorizontalGroup(layout.createParallelGroup(Alignment.LEADING) + .addComponent(getChkGenerateSurprises()) + .addComponent(getChkGenerateMysteryBoxes()) + .addComponent(mysteryBoxPanel)); // TODO : Wave 7 : Surprises panel.setEnabled(false); @@ -2030,36 +2000,33 @@ private void createMysteryBoxPanel(final JPanel panel) { // endregion Initialization // region Options + /** - * Sets the options for this panel to the default for the selected - * CompanyGenerationMethod + * Sets the options for this panel to the default for the selected CompanyGenerationMethod */ public void setOptions() { setOptions(getComboCompanyGenerationMethod().getSelectedItem()); } /** - * Sets the options for this panel to the default for the provided - * CompanyGenerationMethod + * Sets the options for this panel to the default for the provided CompanyGenerationMethod * - * @param method the CompanyGenerationOptions to create the - * CompanyGenerationOptions from + * @param method the CompanyGenerationOptions to create the CompanyGenerationOptions from */ public void setOptions(final CompanyGenerationMethod method) { setOptions(new CompanyGenerationOptions(method)); } /** - * Sets the options for this panel based on the provided - * CompanyGenerationOptions + * Sets the options for this panel based on the provided CompanyGenerationOptions * * @param options the CompanyGenerationOptions to use */ public void setOptions(final CompanyGenerationOptions options) { // Base Information getComboCompanyGenerationMethod().setSelectedItem(options.getMethod()); - getComboSpecifiedFaction() - .setSelectedItem(new FactionDisplay(options.getSpecifiedFaction(), getCampaign().getLocalDate())); + getComboSpecifiedFaction().setSelectedItem(new FactionDisplay(options.getSpecifiedFaction(), + getCampaign().getLocalDate())); getChkGenerateMercenaryCompanyCommandLance().setSelected(options.isGenerateMercenaryCompanyCommandLance()); getSpnCompanyCount().setValue(options.getCompanyCount()); getSpnIndividualLanceCount().setValue(options.getIndividualLanceCount()); @@ -2103,8 +2070,7 @@ public void setOptions(final CompanyGenerationOptions options) { // Units getComboBattleMekFactionGenerationMethod().setSelectedItem(options.getBattleMekFactionGenerationMethod()); - getComboBattleMekWeightClassGenerationMethod() - .setSelectedItem(options.getBattleMekWeightClassGenerationMethod()); + getComboBattleMekWeightClassGenerationMethod().setSelectedItem(options.getBattleMekWeightClassGenerationMethod()); getComboBattleMekQualityGenerationMethod().setSelectedItem(options.getBattleMekQualityGenerationMethod()); getChkNeverGenerateStarLeagueMeks().setSelected(options.isNeverGenerateStarLeagueMeks()); getChkOnlyGenerateStarLeagueMeks().setSelected(options.isOnlyGenerateStarLeagueMeks()); @@ -2122,8 +2088,7 @@ public void setOptions(final CompanyGenerationOptions options) { if (getChkGenerateForceIcons().isSelected() != options.isGenerateForceIcons()) { getChkGenerateForceIcons().doClick(); } - getChkUseSpecifiedFactionToGenerateForceIcons() - .setSelected(options.isUseSpecifiedFactionToGenerateForceIcons()); + getChkUseSpecifiedFactionToGenerateForceIcons().setSelected(options.isUseSpecifiedFactionToGenerateForceIcons()); if (getChkGenerateOriginNodeForceIcon().isSelected() != options.isGenerateOriginNodeForceIcon()) { getChkGenerateOriginNodeForceIcon().doClick(); } @@ -2190,8 +2155,7 @@ public void setOptions(final CompanyGenerationOptions options) { * @return the CompanyGenerationOptions created from the current panel */ public CompanyGenerationOptions createOptionsFromPanel() { - final CompanyGenerationOptions options = new CompanyGenerationOptions( - getComboCompanyGenerationMethod().getSelectedItem()); + final CompanyGenerationOptions options = new CompanyGenerationOptions(getComboCompanyGenerationMethod().getSelectedItem()); // Base Information options.setSpecifiedFaction(Objects.requireNonNull(getComboSpecifiedFaction().getSelectedItem()).getFaction()); @@ -2236,8 +2200,7 @@ public CompanyGenerationOptions createOptionsFromPanel() { // Units options.setBattleMekFactionGenerationMethod(getComboBattleMekFactionGenerationMethod().getSelectedItem()); - options.setBattleMekWeightClassGenerationMethod( - getComboBattleMekWeightClassGenerationMethod().getSelectedItem()); + options.setBattleMekWeightClassGenerationMethod(getComboBattleMekWeightClassGenerationMethod().getSelectedItem()); options.setBattleMekQualityGenerationMethod(getComboBattleMekQualityGenerationMethod().getSelectedItem()); options.setNeverGenerateStarLeagueMeks(getChkNeverGenerateStarLeagueMeks().isSelected()); options.setOnlyGenerateStarLeagueMeks(getChkOnlyGenerateStarLeagueMeks().isSelected()); @@ -2253,8 +2216,7 @@ public CompanyGenerationOptions createOptionsFromPanel() { // Unit options.setForceNamingMethod(getComboForceNamingMethod().getSelectedItem()); options.setGenerateForceIcons(getChkGenerateForceIcons().isSelected()); - options.setUseSpecifiedFactionToGenerateForceIcons( - getChkUseSpecifiedFactionToGenerateForceIcons().isSelected()); + options.setUseSpecifiedFactionToGenerateForceIcons(getChkUseSpecifiedFactionToGenerateForceIcons().isSelected()); options.setGenerateOriginNodeForceIcon(getChkGenerateOriginNodeForceIcon().isSelected()); options.setUseOriginNodeForceIconLogo(getChkUseOriginNodeForceIconLogo().isSelected()); options.setForceWeightLimits(new TreeMap<>()); @@ -2302,10 +2264,10 @@ public CompanyGenerationOptions createOptionsFromPanel() { } /** - * Validates the data contained in this panel, returning the current state of - * validation. + * Validates the data contained in this panel, returning the current state of validation. * * @param display to display dialogs containing the messages or not + * * @return true if the data validates successfully, otherwise false */ public ValidationState validateOptions(final boolean display) { @@ -2313,13 +2275,12 @@ public ValidationState validateOptions(final boolean display) { // Minimum Generation Size Validation // Minimum Generation Parameter of 1 Company or Lance, the Company Command Lance // Doesn't Count - if (((int) getSpnCompanyCount().getValue() <= 0) - && ((int) getSpnIndividualLanceCount().getValue() <= 0)) { + if (((int) getSpnCompanyCount().getValue() <= 0) && ((int) getSpnIndividualLanceCount().getValue() <= 0)) { if (display) { JOptionPane.showMessageDialog(getFrame(), - resources.getString("CompanyGenerationOptionsPanel.InvalidGenerationSize.text"), - resources.getString("InvalidOptions.title"), - JOptionPane.ERROR_MESSAGE); + resources.getString("CompanyGenerationOptionsPanel.InvalidGenerationSize.text"), + resources.getString("InvalidOptions.title"), + JOptionPane.ERROR_MESSAGE); } return ValidationState.FAILURE; } @@ -2365,12 +2326,13 @@ public ValidationState validateOptions(final boolean display) { // endregion Options // region File I/O + /** * Imports CompanyGenerationOptions from an XML file */ public void importOptionsFromXML() { FileDialogs.openCompanyGenerationOptions(getFrame()) - .ifPresent(file -> setOptions(CompanyGenerationOptions.parseFromXML(file))); + .ifPresent(file -> setOptions(CompanyGenerationOptions.parseFromXML(file))); } /** @@ -2378,7 +2340,7 @@ public void importOptionsFromXML() { */ public void exportOptionsToXML() { FileDialogs.saveCompanyGenerationOptions(getFrame()) - .ifPresent(file -> createOptionsFromPanel().writeToFile(file)); + .ifPresent(file -> createOptionsFromPanel().writeToFile(file)); } // endregion File I/O } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelRoleTest.java b/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelRoleTest.java index 1b8efa8056a..ac6a94db096 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelRoleTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelRoleTest.java @@ -27,6 +27,8 @@ */ package mekhq.campaign.personnel.enums; +import static mekhq.campaign.personnel.enums.PersonnelRole.BATTLE_ARMOUR; +import static mekhq.utilities.MHQInternationalization.isResourceKeyValid; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -35,31 +37,29 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.ResourceBundle; import java.util.Set; -import mekhq.MekHQ; import org.junit.jupiter.api.Test; class PersonnelRoleTest { - // region Variable Declarations private static final PersonnelRole[] roles = PersonnelRole.values(); - private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel", - MekHQ.getMHQOptions().getLocale()); - // endregion Variable Declarations + @Test + void testGetLabel_NotClan() { + for (final PersonnelRole personnelRole : roles) { + String label = personnelRole.getLabel(false); + boolean isValid = isResourceKeyValid(label); + assertTrue(isValid, "Invalid resource key: " + label); + } + } - // region Getters @Test - void testGetName() { - assertEquals(resources.getString("PersonnelRole.MEKWARRIOR.text"), PersonnelRole.MEKWARRIOR.getName(false)); - assertEquals(resources.getString("PersonnelRole.MEKWARRIOR.text"), PersonnelRole.MEKWARRIOR.getName(true)); - assertEquals(resources.getString("PersonnelRole.BATTLE_ARMOUR.text"), - PersonnelRole.BATTLE_ARMOUR.getName(false)); - assertEquals(resources.getString("PersonnelRole.BATTLE_ARMOUR.clan.text"), - PersonnelRole.BATTLE_ARMOUR.getName(true)); - assertEquals(resources.getString("PersonnelRole.ADMINISTRATOR_LOGISTICS.text"), - PersonnelRole.ADMINISTRATOR_LOGISTICS.getName(false)); + void testGetLabel_IsClan() { + for (final PersonnelRole personnelRole : roles) { + String label = personnelRole.getLabel(true); + boolean isValid = isResourceKeyValid(label); + assertTrue(isValid, "Invalid resource key: " + label); + } } @Test @@ -75,6 +75,7 @@ void testGetMnemonicEnsureUniqueness() { usedMnemonics.add(role.getMnemonic()); } } + // endregion Getters // region Boolean Comparison Methods @@ -191,7 +192,7 @@ void testIsProtoMekPilot() { @Test void testIsBattleArmour() { for (final PersonnelRole personnelRole : roles) { - if (personnelRole == PersonnelRole.BATTLE_ARMOUR) { + if (personnelRole == BATTLE_ARMOUR) { assertTrue(personnelRole.isBattleArmour()); } else { assertFalse(personnelRole.isBattleArmour()); @@ -519,7 +520,7 @@ void testIsVehicleCrewMember() { @Test void testIsSoldierOrBattleArmour() { for (final PersonnelRole personnelRole : roles) { - if ((personnelRole == PersonnelRole.SOLDIER) || (personnelRole == PersonnelRole.BATTLE_ARMOUR)) { + if ((personnelRole == PersonnelRole.SOLDIER) || (personnelRole == BATTLE_ARMOUR)) { assertTrue(personnelRole.isSoldierOrBattleArmour()); } else { assertFalse(personnelRole.isSoldierOrBattleArmour()); @@ -646,6 +647,34 @@ void testGetMarketableRoles() { assertFalse(militaryRoles.contains(PersonnelRole.NONE)); } + @Test + void testFromString() { + // Valid inputs + assertEquals(PersonnelRole.MEKWARRIOR, PersonnelRole.fromString("MEKWARRIOR")); + assertEquals(PersonnelRole.GROUND_VEHICLE_DRIVER, PersonnelRole.fromString("GROUND_VEHICLE_DRIVER")); + assertEquals(PersonnelRole.ASTECH, PersonnelRole.fromString("ASTECH")); + + // Valid inputs with variations in casing + assertEquals(PersonnelRole.MEKWARRIOR, PersonnelRole.fromString("MekWarrior")); + assertEquals(PersonnelRole.VEHICLE_CREW, PersonnelRole.fromString("vehicle_crew")); + + // Valid inputs with Clan variance + assertEquals(BATTLE_ARMOUR, PersonnelRole.fromString("elemental")); + assertEquals(BATTLE_ARMOUR, PersonnelRole.fromString("Battle Armor Pilot")); + + // Deprecated names + assertEquals(PersonnelRole.MEKWARRIOR, PersonnelRole.fromString("MechWarrior")); + assertEquals(PersonnelRole.PROTOMEK_PILOT, PersonnelRole.fromString("protomek PILOT")); + + // Index input + assertEquals(BATTLE_ARMOUR, PersonnelRole.fromString(BATTLE_ARMOUR.ordinal() + "")); + + // Invalid inputs + assertEquals(PersonnelRole.NONE, PersonnelRole.fromString("INVALID_ROLE")); + assertEquals(PersonnelRole.NONE, PersonnelRole.fromString("")); + assertEquals(PersonnelRole.NONE, PersonnelRole.fromString(null)); + } + @Test void testGetPrimaryRoles() { // This should be all roles bar one, namely PersonnelRole.NONE @@ -693,23 +722,5 @@ void testGetCivilianCount() { // endregion Static Methods // region File I/O - @Test - void testParseFromString() { - // Normal Parsing - assertEquals(PersonnelRole.NONE, PersonnelRole.parseFromString("NONE")); - assertEquals(PersonnelRole.BATTLE_ARMOUR, PersonnelRole.parseFromString("BATTLE_ARMOUR")); - assertEquals(PersonnelRole.ADMINISTRATOR_LOGISTICS, PersonnelRole.parseFromString("ADMINISTRATOR_LOGISTICS")); - - // Error Case - assertEquals(PersonnelRole.NONE, PersonnelRole.parseFromString("28")); - assertEquals(PersonnelRole.NONE, PersonnelRole.parseFromString("blah")); - } // endregion File I/O - - @Test - void testToStringOverride() { - assertEquals(resources.getString("PersonnelRole.MEKWARRIOR.text"), PersonnelRole.MEKWARRIOR.toString()); - assertEquals(resources.getString("PersonnelRole.ADMINISTRATOR_LOGISTICS.text"), - PersonnelRole.ADMINISTRATOR_LOGISTICS.toString()); - } } From df96a91c9c977dcba6035361feb18f1fe5c9d2a4 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 6 Apr 2025 17:56:48 -0500 Subject: [PATCH 06/10] - Introduced a command (`CMD_GENERATE_ROLEPLAY_ATTRIBUTES`) to generate random roleplay attributes for personnel.es. --- .../resources/mekhq/resources/GUI.properties | 3 +- .../personnel/enums/PersonnelRole.java | 62 +++++++++---------- .../generator/DefaultSkillGenerator.java | 6 +- .../adapter/PersonnelTableMouseAdapter.java | 15 +++++ 4 files changed, 49 insertions(+), 37 deletions(-) diff --git a/MekHQ/resources/mekhq/resources/GUI.properties b/MekHQ/resources/mekhq/resources/GUI.properties index daa9dde4489..9fac966fd81 100644 --- a/MekHQ/resources/mekhq/resources/GUI.properties +++ b/MekHQ/resources/mekhq/resources/GUI.properties @@ -281,7 +281,8 @@ removePregnancies.text=Remove Pregnancies regenerateLoyalty.text=Regenerate Loyalty regeneratePersonality.text=Regenerate Personality addRandomSPA.text=Add Random SPA -generateRoleplaySkills.text=Generate Roleplay Skills +generateRoleplaySkills.text=Generate Random Roleplay Skills +generateRoleplayAttributes.text=Generate Random Roleplay Attributes addMinimumComplement.text=Add minimum complement addMinimumComplementRandom.text=Random addMinimumComplementElite.text=Elite diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java b/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java index b629d9a3b1f..e38419c3ac3 100644 --- a/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java +++ b/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelRole.java @@ -47,103 +47,99 @@ public enum PersonnelRole { // region Enum Declarations /** * Individual roles with corresponding name texts and mnemonics. - * - *

Note: The attribute score modifiers assume a default attribute score of 5. I opted to only include - * this information as modifiers, as it means if we ever change what the default score is, the ratios will remain - * correct.

*/ // I used an average of the modifiers from the MekWarrior, Hot Shot, and Grizzled Veteran ATOW Archetypes - MEKWARRIOR(true, KeyEvent.VK_M, -1, 0, 1, 1, -1, -1, -1), + MEKWARRIOR(true, KeyEvent.VK_M, 4, 5, 6, 6, 4, 4, 4), // I used an average of the modifiers from the MekWarrior, and Aerospace Pilot ATOW Archetypes - LAM_PILOT(true, KeyEvent.VK_UNDEFINED, -1, -1, 1, 1, -1, -1, -1), + LAM_PILOT(true, KeyEvent.VK_UNDEFINED, 4, 4, 6, 6, 4, 4, 41), // ATOW: Tanker Archetype - GROUND_VEHICLE_DRIVER(true, KeyEvent.VK_V, -1, 0, 0, +1, -1, -1, -1), + GROUND_VEHICLE_DRIVER(true, KeyEvent.VK_V, 4, 5, 5, 6, 4, 4, 4), // ATOW: Tanker Archetype - NAVAL_VEHICLE_DRIVER(true, KeyEvent.VK_N, -1, 0, 0, +1, -1, -1, -1), + NAVAL_VEHICLE_DRIVER(true, KeyEvent.VK_N, 4, 5, 5, 6, 4, 4, 4), // ATOW: Companion Chopper Pilot Archetype - VTOL_PILOT(true, KeyEvent.VK_UNDEFINED, -1, -1, 0, 0, -1, -1, -1), + VTOL_PILOT(true, KeyEvent.VK_UNDEFINED, 4, 4, 5, 5, 4, 4, 4), // ATOW: Tanker Archetype - VEHICLE_GUNNER(true, KeyEvent.VK_G, -1, 0, 0, +1, -1, -1, -1), + VEHICLE_GUNNER(true, KeyEvent.VK_G, 4, 5, 5, 6, 4, 4, 4), // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the // Technician skill) - VEHICLE_CREW(true, KeyEvent.VK_UNDEFINED, 0, -1, 0, -2, 0, -1, -2), + VEHICLE_CREW(true, KeyEvent.VK_UNDEFINED, 5, 4, 5, 3, 5, 4, 3), // ATOW: Aerospace Pilot Archetype - AEROSPACE_PILOT(true, KeyEvent.VK_A, -3, -2, 0, 0, -1, -1, 0), + AEROSPACE_PILOT(true, KeyEvent.VK_A, 2, 3, 5, 5, 4, 4, 0), // ATOW: Aerospace Pilot Archetype - CONVENTIONAL_AIRCRAFT_PILOT(true, KeyEvent.VK_C, -3, -2, 0, 0, -1, -1, 0), + CONVENTIONAL_AIRCRAFT_PILOT(true, KeyEvent.VK_C, 2, 3, 5, 5, 4, 4, 0), // ATOW: Aerospace Pilot Archetype (most ProtoMek pilots are Aerospace Sibkbo washouts, so this made the most sense) - PROTOMEK_PILOT(true, KeyEvent.VK_P, -3, -2, 0, 0, -1, -1, 0), + PROTOMEK_PILOT(true, KeyEvent.VK_P, 2, 3, 5, 5, 4, 4, 0), // ATOW: Elemental Archetype - BATTLE_ARMOUR(true, true, KeyEvent.VK_B, +2, +1, -1, 0, -2, -1, -2), + BATTLE_ARMOUR(true, true, KeyEvent.VK_B, 7, 6, 4, 0, 3, 4, 3), // ATOW: Renegade Warrior Archetype - SOLDIER(true, KeyEvent.VK_S, 0, 0, -1, 0, -1, +1, -2), + SOLDIER(true, KeyEvent.VK_S, 5, 5, 4, 5, 4, 6, 3), // ATOW: Tanker Archetype - VESSEL_PILOT(true, KeyEvent.VK_I, -1, 0, 0, +1, -1, -1, -1), + VESSEL_PILOT(true, KeyEvent.VK_I, 4, 5, 5, 6, 4, 4, 4), // ATOW: Tanker Archetype - VESSEL_GUNNER(true, KeyEvent.VK_U, -1, 0, 0, +1, -1, -1, -1), + VESSEL_GUNNER(true, KeyEvent.VK_U, 4, 5, 5, 6, 4, 4, 4), // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the // Technician skill) - VESSEL_CREW(true, KeyEvent.VK_W, 0, -1, 0, -2, 0, -1, -2), + VESSEL_CREW(true, KeyEvent.VK_W, 5, 4, 5, 3, 5, 4, 3), // ATOW: Battlefield Tech Archetype - VESSEL_NAVIGATOR(true, KeyEvent.VK_Y, 0, -1, 0, -2, 0, -1, -2), + VESSEL_NAVIGATOR(true, KeyEvent.VK_Y, 5, 4, 5, 3, 5, 4, 3), // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the // Technician skill) - MEK_TECH(false, KeyEvent.VK_T, 0, -1, 0, -2, 0, -1, -2), + MEK_TECH(false, KeyEvent.VK_T, 5, 4, 5, 3, 5, 4, 3), // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the // Technician skill) - MECHANIC(false, KeyEvent.VK_E, 0, -1, 0, -2, 0, -1, -2), + MECHANIC(false, KeyEvent.VK_E, 5, 4, 5, 3, 5, 4, 3), // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the // Technician skill) - AERO_TEK(false, KeyEvent.VK_O, 0, -1, 0, -2, 0, -1, -2), + AERO_TEK(false, KeyEvent.VK_O, 5, 4, 5, 3, 5, 4, 3), // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the // Technician skill) - BA_TECH(false, KeyEvent.VK_UNDEFINED, 0, -1, 0, -2, 0, -1, -2), + BA_TECH(false, KeyEvent.VK_UNDEFINED, 5, 4, 5, 3, 5, 4, 3), // ATOW: Battlefield Tech Archetype (but with the reduced Dexterity removed, as that's a Linked Attribute for the // Technician skill) - ASTECH(false, KeyEvent.VK_UNDEFINED, 0, -1, 0, -2, 0, -1, -2), + ASTECH(false, KeyEvent.VK_UNDEFINED, 5, 4, 5, 3, 5, 4, 3), // ATOW: Communications Specialist Archetype (this might seem like an odd choice, but the Attributes for this Archetype // work really well for this profession - DOCTOR(false, KeyEvent.VK_D, -2, -1, -1, 0, 1, -1, -1), + DOCTOR(false, KeyEvent.VK_D, 3, 4, 4, 5, 6, 4, 4), // ATOW: Communications Specialist Archetype (this might seem like an odd choice, but the Attributes for this Archetype // work really well for this profession - MEDIC(false, KeyEvent.VK_UNDEFINED, -2, -1, -1, 0, 1, -1, -1), + MEDIC(false, KeyEvent.VK_UNDEFINED, 3, 4, 4, 5, 6, 4, 4), // ATOW: Faceman Archetype - ADMINISTRATOR_COMMAND(false, KeyEvent.VK_UNDEFINED, -2, -2, -2, -1, 1, -2, 1), + ADMINISTRATOR_COMMAND(false, KeyEvent.VK_UNDEFINED, 3, 3, 3, 4, 6, 3, 5), // ATOW: Faceman Archetype - ADMINISTRATOR_LOGISTICS(false, KeyEvent.VK_L, -2, -2, -2, -1, 1, -2, 1), + ADMINISTRATOR_LOGISTICS(false, KeyEvent.VK_L, 3, 3, 3, 4, 6, 3, 5), // ATOW: Faceman Archetype - ADMINISTRATOR_TRANSPORT(false, KeyEvent.VK_R, -2, -2, -2, -1, 1, -2, 1), + ADMINISTRATOR_TRANSPORT(false, KeyEvent.VK_R, 3, 3, 3, 4, 6, 3, 5), // ATOW: Faceman Archetype - ADMINISTRATOR_HR(false, KeyEvent.VK_H, -2, -2, -2, -1, 1, -2, 1), + ADMINISTRATOR_HR(false, KeyEvent.VK_H, 3, 3, 3, 4, 6, 3, 5), // No archetype, but ATOW pg 35 states that the Attribute scores for an average person are 4 - DEPENDENT(false, KeyEvent.VK_UNDEFINED, -1, -1, -1, -1, -1, -1, -1), + DEPENDENT(false, KeyEvent.VK_UNDEFINED, 4, 4, 4, 4, 4, 4, 4), // If we're generating a character without a Profession, we're just going to leave them with middle of the road // Attribute scores (5 in everything) @@ -168,7 +164,7 @@ public enum PersonnelRole { // region Constructors PersonnelRole(final int mnemonic) { - this(false, false, mnemonic, 0, 0, 0, 0, 0, 0, 0); + this(false, false, mnemonic, 5, 5, 5, 5, 5, 5, 5); } PersonnelRole(final boolean isCombat, final int mnemonic, final int strength, final int body, final int dexterity, diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java index 93f815e54e4..f984272ec08 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java @@ -170,9 +170,9 @@ public void generateAttributes(Person person) { } // Profession && Phenotype adjustments - int attributeModifier = profession.getAttributeModifier(attribute); - attributeModifier += phenotype.getAttributeModifier(attribute); - person.changeAttributeScore(attribute, attributeModifier); + int baseAttributeScore = profession.getAttributeModifier(attribute); + int attributeModifier = phenotype.getAttributeModifier(attribute); + person.setAttributeScore(attribute, baseAttributeScore + attributeModifier); // Attribute randomization int roll = d6(); diff --git a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java index 5e02956fd21..54707751f26 100644 --- a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java @@ -213,6 +213,7 @@ public class PersonnelTableMouseAdapter extends JPopupMenuAdapter { private static final String CMD_PERSONALITY = "PERSONALITY"; private static final String CMD_ADD_RANDOM_ABILITY = "ADD_RANDOM_ABILITY"; private static final String CMD_GENERATE_ROLEPLAY_SKILLS = "GENERATE_ROLEPLAY_SKILLS"; + private static final String CMD_GENERATE_ROLEPLAY_ATTRIBUTES = "GENERATE_ROLEPLAY_ATTRIBUTES"; private static final String CMD_FREE = "FREE"; private static final String CMD_EXECUTE = "EXECUTE"; @@ -1367,6 +1368,15 @@ public void actionPerformed(ActionEvent action) { } break; } + case CMD_GENERATE_ROLEPLAY_ATTRIBUTES: { + RandomSkillPreferences skillPreferences = getCampaign().getRandomSkillPreferences(); + AbstractSkillGenerator skillGenerator = new DefaultSkillGenerator(skillPreferences); + for (Person person : people) { + skillGenerator.generateAttributes(person); + MekHQ.triggerEvent(new PersonChangedEvent(person)); + } + break; + } // region Randomization Menu case CMD_RANDOM_NAME: { @@ -3631,6 +3641,11 @@ protected Optional createPopupMenu() { menuItem.addActionListener(this); menu.add(menuItem); + menuItem = new JMenuItem(resources.getString("generateRoleplayAttributes.text")); + menuItem.setActionCommand(CMD_GENERATE_ROLEPLAY_ATTRIBUTES); + menuItem.addActionListener(this); + menu.add(menuItem); + JMenu attributesMenu = new JMenu(resources.getString("spendOnAttributes.set")); for (SkillAttribute attribute : SkillAttribute.values()) { From fc015ff6718105082a80915bbf9dd632b7bd2a13 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Sun, 6 Apr 2025 17:57:47 -0500 Subject: [PATCH 07/10] - Changed the `generateRoleplayAttributes.text` label from "Generate Random Roleplay Attributes" to "Reset Roleplay Attributes." --- MekHQ/resources/mekhq/resources/GUI.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MekHQ/resources/mekhq/resources/GUI.properties b/MekHQ/resources/mekhq/resources/GUI.properties index 9fac966fd81..6ece476658a 100644 --- a/MekHQ/resources/mekhq/resources/GUI.properties +++ b/MekHQ/resources/mekhq/resources/GUI.properties @@ -282,7 +282,7 @@ regenerateLoyalty.text=Regenerate Loyalty regeneratePersonality.text=Regenerate Personality addRandomSPA.text=Add Random SPA generateRoleplaySkills.text=Generate Random Roleplay Skills -generateRoleplayAttributes.text=Generate Random Roleplay Attributes +generateRoleplayAttributes.text=Reset Roleplay Attributes addMinimumComplement.text=Add minimum complement addMinimumComplementRandom.text=Random addMinimumComplementElite.text=Elite From e3abdc900584da107692ac2d364e757638269bc6 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Wed, 9 Apr 2025 18:11:40 -0500 Subject: [PATCH 08/10] Added Attribute Maximums Based on Phenotype and Exceptional Attribute SPAs - Introduced a new `AttributeScoreSorter` for comparing "x / y" formatted strings, sorting by primary and secondary values. - Updated attribute display and sorting in `PersonnelTableModelColumn` to represent both current scores and caps ("x / y"). - Added new "Exceptional Attribute" SPAs to increase attribute caps. - Modified phenotype definitions in `Phenotype` to include attribute caps specific to each phenotype. - Adjusted attribute handling logic to respect both phenotype caps and SPA adjustments. - Enhanced unit tests to verify attribute caps and ensure adherence to defined ranges. --- MekHQ/data/universe/defaultspa.xml | 68 +++- .../src/mekhq/campaign/personnel/Person.java | 128 +++--- .../campaign/personnel/PersonnelOptions.java | 16 + .../campaign/personnel/enums/Phenotype.java | 39 +- .../generator/DefaultSkillGenerator.java | 4 +- .../campaign/personnel/skills/Attributes.java | 381 ++++++++++++++---- .../adapter/PersonnelTableMouseAdapter.java | 4 +- .../gui/enums/PersonnelTableModelColumn.java | 46 ++- .../gui/sorter/AttributeScoreSorter.java | 92 +++++ .../personnel/skills/AttributesTest.java | 247 +++++++----- .../enums/PersonnelTableModelColumnTest.java | 124 +++--- 11 files changed, 797 insertions(+), 352 deletions(-) create mode 100644 MekHQ/src/mekhq/gui/sorter/AttributeScoreSorter.java diff --git a/MekHQ/data/universe/defaultspa.xml b/MekHQ/data/universe/defaultspa.xml index 21dd7fc6fa4..a8943dc4f87 100644 --- a/MekHQ/data/universe/defaultspa.xml +++ b/MekHQ/data/universe/defaultspa.xml @@ -469,6 +469,66 @@ 1 + + exceptional_attribute_strength + Exceptional Attribute - Strength (ATOW) + + 200 + 1 + + + + exceptional_attribute_body + Exceptional Attribute - Body (ATOW) + + 200 + 1 + + + + exceptional_attribute_reflexes + Exceptional Attribute - Reflexes (ATOW) + + 200 + 1 + + + + exceptional_attribute_dexterity + Exceptional Attribute - Dexterity (ATOW) + + 200 + 1 + + + + exceptional_attribute_intelligence + Exceptional Attribute - Intelligence (ATOW) + + 200 + 1 + + + + exceptional_attribute_willpower + Exceptional Attribute - Willpower (ATOW) + + 200 + 1 + + eagle_eyes 100 @@ -541,9 +601,9 @@ 1 Unit Reputation is decreased by 1 if this character is the campaign commander. -A pilot who has Combat Sense rolls three dice per initiative check, keeping the top two rolls. + A pilot who has Combat Sense rolls three dice per initiative check, keeping the top two rolls. -If individual initiative is enabled, this penalty applies only to the pilot's unit. Otherwise, the penalty is applied only if the pilot's unit is the force commander. + If individual initiative is enabled, this penalty applies only to the pilot's unit. Otherwise, the penalty is applied only if the pilot's unit is the force commander. atow_combat_sense @@ -554,9 +614,9 @@ If individual initiative is enabled, this penalty applies only to the pilot's un 1 Unit Reputation is increased by 1 if this character is the campaign commander. -A pilot who has Combat Sense rolls three dice per initiative check, keeping the top two rolls. + A pilot who has Combat Sense rolls three dice per initiative check, keeping the top two rolls. -If individual initiative is enabled, this bonus applies only to the pilot's unit. Otherwise, the bonus is applied only if the pilot's unit is the force commander. + If individual initiative is enabled, this bonus applies only to the pilot's unit. Otherwise, the bonus is applied only if the pilot's unit is the force commander. Gunnery/Mek::Veteran diff --git a/MekHQ/src/mekhq/campaign/personnel/Person.java b/MekHQ/src/mekhq/campaign/personnel/Person.java index 17a1d6ee168..39c2a7d2954 100644 --- a/MekHQ/src/mekhq/campaign/personnel/Person.java +++ b/MekHQ/src/mekhq/campaign/personnel/Person.java @@ -34,6 +34,8 @@ import static megamek.common.Compute.randomInt; import static megamek.common.enums.SkillLevel.REGULAR; import static mekhq.campaign.personnel.enums.BloodGroup.getRandomBloodGroup; +import static mekhq.campaign.personnel.skills.Attributes.DEFAULT_ATTRIBUTE_SCORE; +import static mekhq.campaign.personnel.skills.Attributes.MAXIMUM_ATTRIBUTE_SCORE; import static mekhq.campaign.personnel.skills.SkillType.S_ADMIN; import java.io.PrintWriter; @@ -2671,7 +2673,7 @@ public static Person generateInstanceFromXML(Node wn, Campaign campaign, Version } else if (nodeName.equalsIgnoreCase("becomingBondsmanEndDate")) { person.becomingBondsmanEndDate = MHQXMLUtility.parseDate(wn2.getTextContent().trim()); } else if (nodeName.equalsIgnoreCase("phenotype")) { - person.phenotype = Phenotype.parseFromString(wn2.getTextContent().trim()); + person.phenotype = Phenotype.fromString(wn2.getTextContent().trim()); } else if (nodeName.equalsIgnoreCase("bloodname")) { person.bloodname = wn2.getTextContent(); } else if (nodeName.equalsIgnoreCase("biography")) { @@ -4958,67 +4960,72 @@ public Attributes getATOWAttributes() { } /** - * Updates the score of a specific attribute to a new value. + * Updates the score for a specific skill attribute. * - *

This method modifies the score of an attribute specified by the {@link SkillAttribute} parameter. If the - * provided attribute is {@code NONE}, no changes will be made. If the attribute is {@code null}, the method logs an - * error and exits without making changes.

+ *

This method sets the provided score for the given {@link SkillAttribute}. If the attribute is + * null or represents "NONE", the method logs a warning and exits without making any changes.

* - * @param attribute the {@link SkillAttribute} to update. Must not be {@code null}. - * @param newScore the new score to set for the specified attribute. + *

The actual attribute score update is delegated to the underlying attribute handler.

* - * @since 0.50.5 + * @param attribute The {@link SkillAttribute} to be updated. Must not be null or "NONE". + * @param newScore The new score to assign to the specified skill attribute. + * + * @author Illiani + * @since 0.50.05 */ public void setAttributeScore(final SkillAttribute attribute, final int newScore) { - if (attribute == null) { - logger.error("(setAttributeScore) SkillAttribute is null."); + if (attribute == null || attribute == SkillAttribute.NONE) { + logger.warn("(setAttributeScore) SkillAttribute is null or NONE."); return; } - switch (attribute) { - case NONE -> { - } - case STRENGTH -> atowAttributes.setStrength(newScore); - case BODY -> atowAttributes.setBody(newScore); - case REFLEXES -> atowAttributes.setReflexes(newScore); - case DEXTERITY -> atowAttributes.setDexterity(newScore); - case INTELLIGENCE -> atowAttributes.setIntelligence(newScore); - case WILLPOWER -> atowAttributes.setWillpower(newScore); - case CHARISMA -> atowAttributes.setCharisma(newScore); - } - ; + atowAttributes.setAttributeScore(phenotype, options, attribute, newScore); } /** * Retrieves the score of a specified attribute. * - *

The method maps the provided {@link SkillAttribute} to its corresponding attribute in - * the {@link Attributes} object and returns its value. If the {@link SkillAttribute} is {@code NONE}, the method - * returns a score of 0. If the {@code attribute} is {@code null}, an error is logged, and the method returns a - * score of 0.

- * * @param attribute the {@link SkillAttribute} to retrieve the score for. * - * @return the score of the specified attribute, or 0 if the attribute is {@code NONE} or {@code null}. + * @return the score of the specified attribute, or {@link Attributes#DEFAULT_ATTRIBUTE_SCORE} if the attribute is + * {@code NONE} or {@code null}. * * @since 0.50.5 */ public int getAttributeScore(final SkillAttribute attribute) { - if (attribute == null) { - logger.error("(getAttributeScore) SkillAttribute is null."); - return 0; + if (attribute == null || attribute.isNone()) { + logger.error("(getAttributeScore) SkillAttribute is null or NONE."); + return DEFAULT_ATTRIBUTE_SCORE; } - return switch (attribute) { - case NONE -> 0; - case STRENGTH -> atowAttributes.getStrength(); - case BODY -> atowAttributes.getBody(); - case REFLEXES -> atowAttributes.getReflexes(); - case DEXTERITY -> atowAttributes.getDexterity(); - case INTELLIGENCE -> atowAttributes.getIntelligence(); - case WILLPOWER -> atowAttributes.getWillpower(); - case CHARISMA -> atowAttributes.getCharisma(); - }; + return atowAttributes.getAttributeScore(attribute); + } + + /** + * Retrieves the maximum allowed value (cap) for the specified {@link SkillAttribute}. + * + *

If the attribute is {@code null} or marked as {@link SkillAttribute#NONE}, a default maximum attribute score + * is returned, and a warning is logged.

+ * + *

For valid attributes, this method delegates to + * {@link Attributes#getAttributeCap(Phenotype, PersonnelOptions, SkillAttribute)}.

+ * + * @param attribute The {@link SkillAttribute} for which the maximum value is being retrieved. Must not be + * {@code null} or {@link SkillAttribute#NONE}. + * + * @return The maximum allowed value (cap) for the given attribute. Returns the default maximum value if the input + * attribute is invalid. + * + * @author Illiani + * @since 0.50.05 + */ + public int getAttributeCap(final SkillAttribute attribute) { + if (attribute == null || attribute.isNone()) { + logger.warn("(getAttributeCap) SkillAttribute is null or NONE."); + return MAXIMUM_ATTRIBUTE_SCORE; + } + + return atowAttributes.getAttributeCap(phenotype, options, attribute); } /** @@ -5037,35 +5044,32 @@ public void setATOWAttributes(final Attributes atowAttributes) { } /** - * Modifies the score of a specified attribute by applying a delta value. + * Modifies the score of a specified skill attribute by a given delta value. * - *

This method maps the provided {@link SkillAttribute} to its corresponding attribute in the - * {@link Attributes} object and adjusts its value by the specified delta. If the {@code attribute} is {@code NONE}, - * the method does nothing. If {@code attribute} is {@code null}, an error is logged, and the method returns without - * performing any operation.

+ *

This method adjusts the current score of the provided {@link SkillAttribute} by adding the specified delta + * to it. If the attribute is {@code null} or {@link SkillAttribute#NONE}, a warning is logged, and the method exits + * without making any changes.

* - * @param attribute the {@link SkillAttribute} to modify the score for. - * @param delta the value to add to (or subtract from) the current score of the specified attribute. + *

The new score is computed as the sum of the current score and the delta, and it is passed + * to {@link Attributes#setAttributeScore(Phenotype, PersonnelOptions, SkillAttribute, int)} to ensure is compiles + * with the character's minimum and maximum attribute score values.

* - * @since 0.50.5 + * @param attribute The {@link SkillAttribute} whose score is to be modified. Must not be null. + * @param delta The value to add to the current score of the specified skill attribute. + * + * @author Illiani + * @since 0.50.05 */ public void changeAttributeScore(final SkillAttribute attribute, final int delta) { - if (attribute == null) { - logger.error("(changeAttributeScore) SkillAttribute is null."); + if (attribute == null || attribute.isNone()) { + logger.warn("(changeAttributeScore) SkillAttribute is null or NONE."); return; } - switch (attribute) { - case NONE -> { - } - case STRENGTH -> atowAttributes.changeStrength(delta); - case BODY -> atowAttributes.changeBody(delta); - case REFLEXES -> atowAttributes.changeReflexes(delta); - case DEXTERITY -> atowAttributes.changeDexterity(delta); - case INTELLIGENCE -> atowAttributes.changeIntelligence(delta); - case WILLPOWER -> atowAttributes.changeWillpower(delta); - case CHARISMA -> atowAttributes.changeCharisma(delta); - } + int current = atowAttributes.getAttributeScore(attribute); + int newScore = current + delta; + + setAttributeScore(attribute, newScore); } public void resetSkillTypes() { diff --git a/MekHQ/src/mekhq/campaign/personnel/PersonnelOptions.java b/MekHQ/src/mekhq/campaign/personnel/PersonnelOptions.java index 706898bc443..604e6cbfd48 100644 --- a/MekHQ/src/mekhq/campaign/personnel/PersonnelOptions.java +++ b/MekHQ/src/mekhq/campaign/personnel/PersonnelOptions.java @@ -64,6 +64,14 @@ public class PersonnelOptions extends PilotOptions { public static final String TECH_MAINTAINER = "tech_maintainer"; public static final String FLAW_GLASS_JAW = "flaw_glass_jaw"; + public static final String EXCEPTIONAL_ATTRIBUTE_STRENGTH = "exceptional_attribute_strength"; + public static final String EXCEPTIONAL_ATTRIBUTE_BODY = "exceptional_attribute_body"; + public static final String EXCEPTIONAL_ATTRIBUTE_REFLEXES = "exceptional_attribute_reflexes"; + public static final String EXCEPTIONAL_ATTRIBUTE_DEXTERITY = "exceptional_attribute_dexterity"; + public static final String EXCEPTIONAL_ATTRIBUTE_INTELLIGENCE = "exceptional_attribute_intelligence"; + public static final String EXCEPTIONAL_ATTRIBUTE_WILLPOWER = "exceptional_attribute_willpower"; + public static final String EXCEPTIONAL_ATTRIBUTE_CHARISMA = "exceptional_attribute_charisma"; + @Override public void initialize() { super.initialize(); @@ -108,6 +116,14 @@ public void initialize() { addOption(l3a, TECH_MAINTAINER, false); addOption(l3a, FLAW_GLASS_JAW, false); + addOption(l3a, EXCEPTIONAL_ATTRIBUTE_STRENGTH, false); + addOption(l3a, EXCEPTIONAL_ATTRIBUTE_BODY, false); + addOption(l3a, EXCEPTIONAL_ATTRIBUTE_REFLEXES, false); + addOption(l3a, EXCEPTIONAL_ATTRIBUTE_DEXTERITY, false); + addOption(l3a, EXCEPTIONAL_ATTRIBUTE_INTELLIGENCE, false); + addOption(l3a, EXCEPTIONAL_ATTRIBUTE_WILLPOWER, false); + addOption(l3a, EXCEPTIONAL_ATTRIBUTE_CHARISMA, false); + addOption(edge, EDGE_MEDICAL, true); addOption(edge, EDGE_REPAIR_BREAK_PART, true); addOption(edge, EDGE_REPAIR_FAILED_REFIT, true); diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java b/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java index 4e90525dd70..4a9b498f723 100644 --- a/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java +++ b/MekHQ/src/mekhq/campaign/personnel/enums/Phenotype.java @@ -34,6 +34,7 @@ import megamek.codeUtilities.MathUtility; import megamek.logging.MMLogger; +import mekhq.campaign.personnel.skills.Attributes; import mekhq.campaign.personnel.skills.enums.SkillAttribute; /** @@ -46,24 +47,24 @@ public enum Phenotype { /** * Individual external phenotypes. */ - MEKWARRIOR(true, true, 0, 0, 1, 1, new ArrayList<>()), - ELEMENTAL(true, true, 2, 1, -1, 0, List.of("atow_toughness")), - AEROSPACE(true, true, -1, -1, +2, +2, List.of("flaw_glass_jaw")), + MEKWARRIOR(true, true, 0, 0, 1, 1, new Attributes(8, 8, 9, 9, 8, 8, 9), new ArrayList<>()), + ELEMENTAL(true, true, 2, 1, -1, 0, new Attributes(9, 9, 8, 7, 8, 9, 8), List.of("atow_toughness")), + AEROSPACE(true, true, -1, -1, +2, +2, new Attributes(7, 7, 9, 9, 9, 8, 8), List.of("flaw_glass_jaw")), // ATOW doesn't cover a vehicle phenotype, but as the linked attributes for vehicle skills are also reflexes and - // dexterity I copied the MekWarrior phenotype - VEHICLE(true, true, 0, 0, 1, 1, new ArrayList<>()), + // dexterity, I copied the MekWarrior phenotype + VEHICLE(true, true, 0, 0, 1, 1, new Attributes(8, 8, 9, 9, 8, 8, 9), new ArrayList<>()), // According to my research, ProtoMek pilots are normally just Aerospace washouts, so I'm assuming they'd have the // same phenotype modifiers. - PROTOMEK(true, true, -1, -1, +2, +2, List.of("flaw_glass_jaw")), + PROTOMEK(true, true, -1, -1, +2, +2, new Attributes(7, 7, 9, 9, 9, 8, 8), List.of("flaw_glass_jaw")), // Copying the MekWarrior phenotype, same reasons as above. - NAVAL(true, true, 0, 0, 1, 1, new ArrayList<>()), + NAVAL(true, true, 0, 0, 1, 1, new Attributes(8, 8, 9, 9, 8, 8, 9), new ArrayList<>()), /** * Individual internal phenotypes. */ // Internal Phenotypes - NONE(false, false, 0, 0, 0, 0, new ArrayList<>()), - GENERAL(false, false, 0, 0, 0, 0, new ArrayList<>()); + NONE(false, false, 0, 0, 0, 0, new Attributes(8, 8, 8, 8, 8, 8, 9), new ArrayList<>()), + GENERAL(false, false, 0, 0, 0, 0, new Attributes(8, 8, 8, 8, 8, 8, 9), new ArrayList<>()); // endregion Enum Declarations // region Variable Declarations @@ -76,28 +77,44 @@ public enum Phenotype { private final int body; private final int reflexes; private final int dexterity; + private final Attributes attributeCaps; private final List bonusTraits; // endregion Variable Declarations // region Constructors Phenotype() { - this(false, false, 0, 0, 0, 0, new ArrayList<>()); + this(false, false, 0, 0, 0, 0, new Attributes(8, 8, 8, 8, 8, 8, 9), new ArrayList<>()); } Phenotype(final boolean isTrueborn, final boolean external, final int strength, final int body, final int reflexes, - final int dexterity, final List bonusTraits) { + final int dexterity, final Attributes attributeCaps, final List bonusTraits) { this.isTrueborn = isTrueborn; this.external = external; this.strength = strength; this.body = body; this.reflexes = reflexes; this.dexterity = dexterity; + this.attributeCaps = attributeCaps; this.bonusTraits = bonusTraits; } // endregion Constructors // region Getters + /** + * Retrieves the cap (maximum allowable score) for a specified {@link SkillAttribute}. + * + * @param attribute The {@link SkillAttribute} for which the cap is requested. + * + * @return The cap value for the specified skill attribute. + * + * @author Illiani + * @since 0.50.05 + */ + public int getAttributeCap(SkillAttribute attribute) { + return attributeCaps.getAttributeScore(attribute); + } + /** * @deprecated use {@link #getLabel()} instead */ diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java index f984272ec08..5f998bea9da 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java @@ -28,7 +28,6 @@ package mekhq.campaign.personnel.generator; import static megamek.common.Compute.d6; -import static mekhq.campaign.personnel.skills.Attributes.MAXIMUM_ATTRIBUTE_SCORE; import static mekhq.campaign.personnel.skills.Attributes.MINIMUM_ATTRIBUTE_SCORE; import static mekhq.campaign.personnel.skills.SkillDeprecationTool.DEPRECATED_SKILLS; import static mekhq.campaign.personnel.skills.enums.SkillSubType.SUPPORT_COMMAND; @@ -189,7 +188,8 @@ public void generateAttributes(Person person) { person.changeAttributeScore(attribute, 1); if (extraRandomAttributes) { - while ((d6() == 6) && (person.getAttributeScore(attribute) < MAXIMUM_ATTRIBUTE_SCORE)) { + int attributeCap = person.getPhenotype().getAttributeCap(attribute); + while ((d6() == 6) && (person.getAttributeScore(attribute) < attributeCap)) { person.changeAttributeScore(attribute, 1); } } diff --git a/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java b/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java index d54cca120f0..b7f9cab27fb 100644 --- a/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java +++ b/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java @@ -28,11 +28,21 @@ package mekhq.campaign.personnel.skills; import static megamek.codeUtilities.MathUtility.clamp; +import static mekhq.campaign.personnel.PersonnelOptions.EXCEPTIONAL_ATTRIBUTE_BODY; +import static mekhq.campaign.personnel.PersonnelOptions.EXCEPTIONAL_ATTRIBUTE_CHARISMA; +import static mekhq.campaign.personnel.PersonnelOptions.EXCEPTIONAL_ATTRIBUTE_DEXTERITY; +import static mekhq.campaign.personnel.PersonnelOptions.EXCEPTIONAL_ATTRIBUTE_INTELLIGENCE; +import static mekhq.campaign.personnel.PersonnelOptions.EXCEPTIONAL_ATTRIBUTE_REFLEXES; +import static mekhq.campaign.personnel.PersonnelOptions.EXCEPTIONAL_ATTRIBUTE_STRENGTH; +import static mekhq.campaign.personnel.PersonnelOptions.EXCEPTIONAL_ATTRIBUTE_WILLPOWER; import java.io.PrintWriter; import megamek.codeUtilities.MathUtility; import megamek.logging.MMLogger; +import mekhq.campaign.personnel.PersonnelOptions; +import mekhq.campaign.personnel.enums.Phenotype; +import mekhq.campaign.personnel.skills.enums.SkillAttribute; import mekhq.utilities.MHQXMLUtility; import org.w3c.dom.Node; import org.w3c.dom.NodeList; @@ -117,161 +127,303 @@ public Attributes() { charisma = DEFAULT_ATTRIBUTE_SCORE; } + + /** + * Creates an instance of {@code Attributes} with specified values for each {@link SkillAttribute}. + * + * @param strength The initial value for the strength {@link SkillAttribute}. + * @param body The initial value for the body {@link SkillAttribute}. + * @param reflexes The initial value for the reflexes {@link SkillAttribute}. + * @param dexterity The initial value for the dexterity {@link SkillAttribute}. + * @param intelligence The initial value for the intelligence {@link SkillAttribute}. + * @param willpower The initial value for the willpower {@link SkillAttribute}. + * @param charisma The initial value for the charisma {@link SkillAttribute}. + * + * @author Illiani + * @since 0.50.05 + */ + public Attributes(int strength, int body, int reflexes, int dexterity, int intelligence, int willpower, + int charisma) { + this.strength = strength; + this.body = body; + this.reflexes = reflexes; + this.dexterity = dexterity; + this.intelligence = intelligence; + this.willpower = willpower; + this.charisma = charisma; + } + + /** + * Initializes all attributes with the same specified value. + * + *

This constructor is primarily intended to facilitate testing by allowing all attribute fields to be set + * to the same value with a single argument.

+ * + * @param singleValue The value to be assigned to all attribute fields, such as strength, body, reflexes, dexterity, + * intelligence, willpower, and charisma. + * + * @author Illiani + * @since 0.50.05 + */ + public Attributes(int singleValue) { + this.strength = singleValue; + this.body = singleValue; + this.reflexes = singleValue; + this.dexterity = singleValue; + this.intelligence = singleValue; + this.willpower = singleValue; + this.charisma = singleValue; + } + // Getters and Setters /** - * @return the current strength value. + * Retrieves the score for a given attribute. * - * @since 0.50.5 + *

If an invalid or unsupported {@link SkillAttribute} is provided, this method logs an error and returns a + * default value of {@link Attributes#DEFAULT_ATTRIBUTE_SCORE}.

+ * + * @param attribute The {@link SkillAttribute} whose score should be retrieved. + * + * @return The score corresponding to the specified skill attribute, or {@link Attributes#DEFAULT_ATTRIBUTE_SCORE} + * if the attribute is invalid. + * + * @author Illiani + * @since 0.50.05 */ - public int getStrength() { - return strength; + public int getAttributeScore(SkillAttribute attribute) { + if (attribute == null || attribute.isNone()) { + logger.warn("(getAttributeScore) attribute is null or NONE."); + return DEFAULT_ATTRIBUTE_SCORE; + } + + return switch (attribute) { + case STRENGTH -> strength; + case BODY -> body; + case REFLEXES -> reflexes; + case DEXTERITY -> dexterity; + case INTELLIGENCE -> intelligence; + case WILLPOWER -> willpower; + case CHARISMA -> charisma; + default -> { + logger.error("(getAttributeScore) Invalid attribute requested: {}", attribute); + yield DEFAULT_ATTRIBUTE_SCORE; + } + }; } /** - * Sets the strength attribute, ensuring it is clamped between the defined minimum and maximum values. + * Sets the score for a specific skill attribute while respecting its attribute cap. * - * @param strength the new strength value. + *

This method updates the score of the provided {@link SkillAttribute} to the specified value. The score is + * clamped to ensure it remains within the range of {@link Attributes#MINIMUM_ATTRIBUTE_SCORE} and the calculated + * cap for the attribute.

* - * @see #MINIMUM_ATTRIBUTE_SCORE - * @see #MAXIMUM_ATTRIBUTE_SCORE - * @since 0.50.5 + *

The attribute cap is determined using the provided {@link Phenotype} and may be further influenced by + * {@link PersonnelOptions}, such as special abilities or conditions modifying the cap.

+ * + *

If the provided {@link SkillAttribute} is null, represents "NONE", or is unrecognized, + * the method logs a warning or error and exits without making any changes.

+ * + * @param phenotype The {@link Phenotype} object used to derive the attribute cap for the skill. + * @param options The {@link PersonnelOptions} containing context-specific modifiers (e.g., special abilities). + * @param attribute The {@link SkillAttribute} representing the attribute to update. Must not be null + * or "NONE". + * @param score The new score to set for the specified attribute. This value will be clamped within + * {@link Attributes#MINIMUM_ATTRIBUTE_SCORE} and the calculated cap. + * + * @author Illiani + * @since 0.50.05 + */ + public void setAttributeScore(Phenotype phenotype, PersonnelOptions options, SkillAttribute attribute, int score) { + if (attribute == null || attribute.isNone()) { + logger.warn("(setAttributeScore) attribute is null or NONE."); + return; + } + + int cap = getAttributeCap(phenotype, options, attribute); + + // This ensures we never fall outside the hard boundaries, no matter how many SPAs or other weirdness the + // player piles on. + cap = clamp(cap, MINIMUM_ATTRIBUTE_SCORE, MAXIMUM_ATTRIBUTE_SCORE); + + switch (attribute) { + case STRENGTH -> strength = clamp(score, MINIMUM_ATTRIBUTE_SCORE, cap); + case BODY -> body = clamp(score, MINIMUM_ATTRIBUTE_SCORE, cap); + case REFLEXES -> reflexes = clamp(score, MINIMUM_ATTRIBUTE_SCORE, cap); + case DEXTERITY -> dexterity = clamp(score, MINIMUM_ATTRIBUTE_SCORE, cap); + case INTELLIGENCE -> intelligence = clamp(score, MINIMUM_ATTRIBUTE_SCORE, cap); + case WILLPOWER -> willpower = clamp(score, MINIMUM_ATTRIBUTE_SCORE, cap); + case CHARISMA -> charisma = clamp(score, MINIMUM_ATTRIBUTE_SCORE, cap); + default -> logger.error("(setAttributeScore) Invalid attribute requested: {}", attribute); + } + } + + /** + * Determines the maximum allowable value (cap) for the specified skill attribute. + * + *

This method calculates the cap for the given {@link SkillAttribute} based on the provided {@link Phenotype} + * and {@link PersonnelOptions}. The base cap is retrieved from the {@code phenotype}, and adjustments are applied + * if the character has specific traits (flags in {@code options}) that raise the cap for certain attributes.

+ * + *

If the attribute is invalid or unrecognized, an error message is logged, and a default value of {@code 0} is + * used.

+ * + * @param phenotype The {@link Phenotype} that provides the base cap for the given attribute. Must not be + * null. + * @param options The {@link PersonnelOptions} that may modify the attribute cap based on specific traits. Must + * not be null. + * @param attribute The {@link SkillAttribute} whose maximum value is being determined. Must not be + * null. + * + * @return The maximum allowable value (cap) for the given attribute, based on the phenotype and applicable trait + * modifiers. + * + * @author Illiani + * @since 0.50.05 + */ + public int getAttributeCap(Phenotype phenotype, PersonnelOptions options, SkillAttribute attribute) { + // This determines the base cap + int cap = phenotype.getAttributeCap(attribute); + + // This is where you'd use options to verify if a character has SPAs that modify their maximum attribute score + boolean hasExceptionalStrength = options.booleanOption(EXCEPTIONAL_ATTRIBUTE_STRENGTH); + boolean hasFreakishStrength = options.booleanOption(MUTATION_FREAKISH_STRENGTH); + boolean hasExceptionalBody = options.booleanOption(EXCEPTIONAL_ATTRIBUTE_BODY); + boolean hasExceptionalReflexes = options.booleanOption(EXCEPTIONAL_ATTRIBUTE_REFLEXES); + boolean hasExceptionalDexterity = options.booleanOption(EXCEPTIONAL_ATTRIBUTE_DEXTERITY); + boolean hasExceptionalIntelligence = options.booleanOption(EXCEPTIONAL_ATTRIBUTE_INTELLIGENCE); + boolean hasExceptionalWillpower = options.booleanOption(EXCEPTIONAL_ATTRIBUTE_WILLPOWER); + boolean hasExceptionalCharisma = options.booleanOption(EXCEPTIONAL_ATTRIBUTE_CHARISMA); + + cap += switch (attribute) { + case STRENGTH -> { + int modifier = hasExceptionalStrength ? 1 : 0; + modifier += hasFreakishStrength ? 1 : 0; + yield modifier; + } + case BODY -> hasExceptionalBody ? 1 : 0; + case DEXTERITY -> hasExceptionalReflexes ? 1 : 0; + case REFLEXES -> hasExceptionalDexterity ? 1 : 0; + case INTELLIGENCE -> hasExceptionalIntelligence ? 1 : 0; + case WILLPOWER -> hasExceptionalWillpower ? 1 : 0; + case CHARISMA -> hasExceptionalCharisma ? 1 : 0; + default -> { + logger.error("(setAttributeScore) Invalid attribute requested for cap modifier: {}", attribute); + yield 0; + } + }; + return cap; + } + + /** + * @deprecated use {@link #getAttributeScore(SkillAttribute)} instead + */ + @Deprecated(since = "0.50.5", forRemoval = true) + public int getStrength() { + return strength; + } + + /** + * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. */ + @Deprecated(since = "0.50.5", forRemoval = true) public void setStrength(int strength) { this.strength = clamp(strength, MINIMUM_ATTRIBUTE_SCORE, MAXIMUM_ATTRIBUTE_SCORE); } /** - * @return the current body value. - * - * @since 0.50.5 + * @deprecated use {@link #getAttributeScore(SkillAttribute)} instead */ + @Deprecated(since = "0.50.5", forRemoval = true) public int getBody() { return body; } - /** - * Sets the body attribute, ensuring it is clamped between the defined minimum and maximum values. - * - * @param body the new body value. - * - * @see #MINIMUM_ATTRIBUTE_SCORE - * @see #MAXIMUM_ATTRIBUTE_SCORE - * @since 0.50.5 + * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. */ + @Deprecated(since = "0.50.5", forRemoval = true) public void setBody(int body) { this.body = clamp(body, MINIMUM_ATTRIBUTE_SCORE, MAXIMUM_ATTRIBUTE_SCORE); } /** - * @return the current reflexes value. - * - * @since 0.50.5 + * @deprecated use {@link #getAttributeScore(SkillAttribute)} instead */ + @Deprecated(since = "0.50.5", forRemoval = true) public int getReflexes() { return reflexes; } - /** - * Sets the reflexes attribute, ensuring it is clamped between the defined minimum and maximum values. - * - * @param reflexes the new reflexes value. - * - * @see #MINIMUM_ATTRIBUTE_SCORE - * @see #MAXIMUM_ATTRIBUTE_SCORE - * @since 0.50.5 + * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. */ + @Deprecated(since = "0.50.5", forRemoval = true) public void setReflexes(int reflexes) { this.reflexes = clamp(reflexes, MINIMUM_ATTRIBUTE_SCORE, MAXIMUM_ATTRIBUTE_SCORE); } /** - * @return the current dexterity value. - * - * @since 0.50.5 + * @deprecated use {@link #getAttributeScore(SkillAttribute)} instead */ + @Deprecated(since = "0.50.5", forRemoval = true) public int getDexterity() { return dexterity; } - /** - * Sets the dexterity attribute, ensuring it is clamped between the defined minimum and maximum values. - * - * @param dexterity the new dexterity value. - * - * @see #MINIMUM_ATTRIBUTE_SCORE - * @see #MAXIMUM_ATTRIBUTE_SCORE - * @since 0.50.5 + * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. */ + @Deprecated(since = "0.50.5", forRemoval = true) public void setDexterity(int dexterity) { this.dexterity = clamp(dexterity, MINIMUM_ATTRIBUTE_SCORE, MAXIMUM_ATTRIBUTE_SCORE); } /** - * @return the current intelligence value. - * - * @since 0.50.5 + * @deprecated use {@link #getAttributeScore(SkillAttribute)} instead */ + @Deprecated(since = "0.50.5", forRemoval = true) public int getIntelligence() { return intelligence; } /** - * Sets the intelligence attribute, ensuring it is clamped between the defined minimum and maximum values. - * - * @param intelligence the new intelligence value. - * - * @see #MINIMUM_ATTRIBUTE_SCORE - * @see #MAXIMUM_ATTRIBUTE_SCORE - * @since 0.50.5 + * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. */ + @Deprecated(since = "0.50.5", forRemoval = true) public void setIntelligence(int intelligence) { this.intelligence = clamp(intelligence, MINIMUM_ATTRIBUTE_SCORE, MAXIMUM_ATTRIBUTE_SCORE); } /** - * @return the current willpower value. - * - * @since 0.50.5 + * @deprecated use {@link #getAttributeScore(SkillAttribute)} instead */ + @Deprecated(since = "0.50.5", forRemoval = true) public int getWillpower() { return willpower; } /** - * Sets the willpower attribute, ensuring it is clamped between the defined minimum and maximum values. - * - * @param willpower the new willpower value. - * - * @see #MINIMUM_ATTRIBUTE_SCORE - * @see #MAXIMUM_ATTRIBUTE_SCORE - * @since 0.50.5 + * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. */ + @Deprecated(since = "0.50.5", forRemoval = true) public void setWillpower(int willpower) { this.willpower = clamp(willpower, MINIMUM_ATTRIBUTE_SCORE, MAXIMUM_ATTRIBUTE_SCORE); } /** - * @return the current charisma value. - * - * @since 0.50.5 + * @deprecated use {@link #getAttributeScore(SkillAttribute)} instead */ + @Deprecated(since = "0.50.5", forRemoval = true) public int getCharisma() { return charisma; } /** - * Sets the charisma attribute, ensuring it is clamped between the defined minimum and maximum values. - * - * @param charisma the new charisma value. - * - * @see #MINIMUM_ATTRIBUTE_SCORE - * @see #MAXIMUM_ATTRIBUTE_SCORE - * @since 0.50.5 + * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. */ + @Deprecated(since = "0.50.5", forRemoval = true) public void setCharisma(int charisma) { this.charisma = clamp(charisma, MINIMUM_ATTRIBUTE_SCORE, MAXIMUM_ATTRIBUTE_SCORE); } @@ -279,22 +431,82 @@ public void setCharisma(int charisma) { // Utility Methods /** - * Applies a delta to all attributes, incrementing or decrementing their values by the specified amount while - * clamping results within bounds. + * Adjusts the scores of all skill attributes by a specified delta. + * + *

This method iterates through all available {@link SkillAttribute} values and applies the given delta to each, + * modifying their scores accordingly. Attribute values are clamped within valid bounds as defined by the + * {@code changeAttribute} and {@code setAttributeScore} methods, ensuring no invalid scores are set.

* - * @param delta the value to add to each attribute. A positive delta will increase the attribute scores, while a - * negative delta will decrease them. + *

Attributes marked as {@link SkillAttribute#NONE} are skipped during the iteration and are not modified.

* + * @param phenotype The {@link Phenotype} used to determine the caps for all skill attributes. + * @param options The {@link PersonnelOptions} containing context-specific modifiers that may affect attribute + * caps. + * @param delta The value to adjust each attribute's score. A positive delta increases the scores, while a + * negative delta decreases them. + * + * @author Illiani * @since 0.50.5 */ - public void changeAllAttributes(int delta) { - changeStrength(delta); - changeBody(delta); - changeReflexes(delta); - changeDexterity(delta); - changeIntelligence(delta); - changeWillpower(delta); - changeCharisma(delta); + public void changeAllAttributes(Phenotype phenotype, PersonnelOptions options, int delta) { + if (phenotype == null) { + logger.warn("(changeAllAttributes) phenotype is null."); + return; + } + + if (options == null) { + logger.warn("(changeAllAttributes) options is null."); + return; + } + + for (SkillAttribute attribute : SkillAttribute.values()) { + if (attribute.isNone()) { + continue; + } + + changeAttribute(phenotype, options, attribute, delta); + } + } + + /** + * Adjusts the score of a specified skill attribute by a given delta. + * + *

This method modifies the score of the provided {@link SkillAttribute} by adding the given delta to its + * current score. The updated score is passed to + * {@link #setAttributeScore(Phenotype, PersonnelOptions, SkillAttribute, int)} to ensure it complies with the + * applicable constraints and caps defined by the provided {@link Phenotype} and {@link PersonnelOptions}.

+ * + *

If the {@code phenotype}, {@code options}, or {@code attribute} is null, or if the attribute + * represents "NONE", the method logs a warning and exits without making any changes.

+ * + * @param phenotype The {@link Phenotype} used to determine the cap for the skill attribute. Must not be + * null. + * @param options The {@link PersonnelOptions} containing context-specific modifiers that may influence the cap + * for the attribute. Must not be null. + * @param attribute The {@link SkillAttribute} whose score is to be modified. Must not be null or + * "NONE". + * @param delta The value to adjust the current score of the attribute, where a positive value increases the + * score and a negative value decreases it. + */ + public void changeAttribute(Phenotype phenotype, PersonnelOptions options, SkillAttribute attribute, int delta) { + if (phenotype == null) { + logger.warn("(changeAttribute) phenotype is null."); + return; + } + if (options == null) { + logger.warn("(changeAttribute) options is null."); + return; + } + + if (attribute == null || attribute.isNone()) { + logger.warn("(changeAttribute) attribute is null or NONE."); + return; + } + + int currentScore = getAttributeScore(attribute); + // We defer ensuring this falls within permissible values to setAttributeScore + int newScore = currentScore + delta; + setAttributeScore(phenotype, options, attribute, newScore); } /** @@ -307,6 +519,7 @@ public void changeAllAttributes(int delta) { * * @since 0.50.5 */ + @Deprecated(since = "0.50.5", forRemoval = true) public void changeStrength(int delta) { strength += delta; strength = clamp(strength, MINIMUM_ATTRIBUTE_SCORE, MAXIMUM_ATTRIBUTE_SCORE); diff --git a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java index 54707751f26..b28c3e6c4b4 100644 --- a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java @@ -27,6 +27,7 @@ */ package mekhq.gui.adapter; +import static java.lang.Math.min; import static java.lang.Math.round; import static megamek.client.ui.WrapLayout.wordWrap; import static megamek.common.Compute.d6; @@ -2838,7 +2839,8 @@ protected Optional createPopupMenu() { String.valueOf(attribute), String.valueOf(attributeCost))); menuItem.addActionListener(this); - menuItem.setEnabled(target <= MAXIMUM_ATTRIBUTE_SCORE && person.getXP() >= attributeCost); + int attributeCap = min(person.getPhenotype().getAttributeCap(attribute), MAXIMUM_ATTRIBUTE_SCORE); + menuItem.setEnabled(target <= attributeCap && person.getXP() >= attributeCost); attributesMenuIncrease.add(menuItem); } menu.add(attributesMenuIncrease); diff --git a/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java b/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java index c5aca49e5ce..1a91c583983 100644 --- a/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java +++ b/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java @@ -60,6 +60,7 @@ import mekhq.campaign.randomEvents.personalities.enums.Social; import mekhq.campaign.unit.Unit; import mekhq.campaign.universe.Planet; +import mekhq.gui.sorter.AttributeScoreSorter; import mekhq.gui.sorter.BonusSorter; import mekhq.gui.sorter.DateStringComparator; import mekhq.gui.sorter.FormattedNumberSorter; @@ -531,7 +532,12 @@ public boolean isPersonality() { public String getCellValue(final Campaign campaign, final PersonnelMarket personnelMarket, final Person person, final boolean loadAssignmentFromMarket, final boolean groupByUnit) { - PersonnelOptions options = person.getOptions(); + final PersonnelOptions options = person.getOptions(); + + // We define these here, as they're used in multiple cases + int currentAttributeValue; + int attributeCap; + String sign; switch (this) { @@ -895,19 +901,33 @@ public String getCellValue(final Campaign campaign, final PersonnelMarket person return String.valueOf(reasoning.ordinal()); case STRENGTH: - return String.valueOf(person.getAttributeScore(SkillAttribute.STRENGTH)); + currentAttributeValue = person.getAttributeScore(SkillAttribute.STRENGTH); + attributeCap = person.getAttributeCap(SkillAttribute.STRENGTH); + return currentAttributeValue + " / " + attributeCap; case BODY: - return String.valueOf(person.getAttributeScore(SkillAttribute.BODY)); + currentAttributeValue = person.getAttributeScore(SkillAttribute.BODY); + attributeCap = person.getAttributeCap(SkillAttribute.BODY); + return currentAttributeValue + " / " + attributeCap; case REFLEXES: - return String.valueOf(person.getAttributeScore(SkillAttribute.REFLEXES)); + currentAttributeValue = person.getAttributeScore(SkillAttribute.REFLEXES); + attributeCap = person.getAttributeCap(SkillAttribute.REFLEXES); + return currentAttributeValue + " / " + attributeCap; case DEXTERITY: - return String.valueOf(person.getAttributeScore(SkillAttribute.DEXTERITY)); + currentAttributeValue = person.getAttributeScore(SkillAttribute.DEXTERITY); + attributeCap = person.getAttributeCap(SkillAttribute.DEXTERITY); + return currentAttributeValue + " / " + attributeCap; case INTELLIGENCE: - return String.valueOf(person.getAttributeScore(SkillAttribute.INTELLIGENCE)); + currentAttributeValue = person.getAttributeScore(SkillAttribute.INTELLIGENCE); + attributeCap = person.getAttributeCap(SkillAttribute.INTELLIGENCE); + return currentAttributeValue + " / " + attributeCap; case WILLPOWER: - return String.valueOf(person.getAttributeScore(SkillAttribute.WILLPOWER)); + currentAttributeValue = person.getAttributeScore(SkillAttribute.WILLPOWER); + attributeCap = person.getAttributeCap(SkillAttribute.WILLPOWER); + return currentAttributeValue + " / " + attributeCap; case CHARISMA: - return String.valueOf(person.getAttributeScore(SkillAttribute.CHARISMA)); + currentAttributeValue = person.getAttributeScore(SkillAttribute.CHARISMA); + attributeCap = person.getAttributeCap(SkillAttribute.CHARISMA); + return currentAttributeValue + " / " + attributeCap; default: return "UNIMPLEMENTED"; } @@ -1156,14 +1176,8 @@ public Comparator getComparator(final Campaign campaign) { SPA_COUNT, IMPLANT_COUNT, LOYALTY, - REASONING, - STRENGTH, - BODY, - REFLEXES, - DEXTERITY, - INTELLIGENCE, - WILLPOWER, - CHARISMA -> new IntegerStringSorter(); + REASONING -> new IntegerStringSorter(); + case STRENGTH, BODY, REFLEXES, DEXTERITY, INTELLIGENCE, WILLPOWER, CHARISMA -> new AttributeScoreSorter(); case SALARY -> new FormattedNumberSorter(); default -> new NaturalOrderComparator(); }; diff --git a/MekHQ/src/mekhq/gui/sorter/AttributeScoreSorter.java b/MekHQ/src/mekhq/gui/sorter/AttributeScoreSorter.java new file mode 100644 index 00000000000..c2878babe6a --- /dev/null +++ b/MekHQ/src/mekhq/gui/sorter/AttributeScoreSorter.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2025 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 (GPL), + * version 3 or (at your option) any later version, + * as published by the Free Software Foundation. + * + * 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. + * + * A copy of the GPL should have been included with this project; + * if not, see . + * + * NOTICE: The MegaMek organization is a non-profit group of volunteers + * creating free software for the BattleTech community. + * + * MechWarrior, BattleMech, `Mech and AeroTech are registered trademarks + * of The Topps Company, Inc. All Rights Reserved. + * + * Catalyst Game Labs and the Catalyst Game Labs logo are trademarks of + * InMediaRes Productions, LLC. + */ +package mekhq.gui.sorter; + +import java.util.Comparator; + +import megamek.codeUtilities.MathUtility; +import megamek.logging.MMLogger; + +/** + * A comparator implementation for sorting strings representing numerical scores in the format "x / y". The sorting is + * performed by the first number `x`, and in case of ties, by the second number `y`. + * + *

This class is typically used for sorting a character's + * {@link mekhq.campaign.personnel.skills.enums.SkillAttribute} scores for display in + * {@link mekhq.gui.enums.PersonnelTableModelColumn}.

+ */ +public class AttributeScoreSorter implements Comparator { + private static final MMLogger logger = MMLogger.create(AttributeScoreSorter.class); + + private final String DIVIDER_STRING = " / "; + + /** + * Compares two strings in the format "x / y" for sorting. + * + *

The comparison is performed as follows:

+ *
    + *
  1. The first numbers (`x` values) from both strings are compared numerically.
  2. + *
  3. If the `x` values are the same, the second numbers (`y` values) are compared numerically.
  4. + *
+ * + * @param firstString the first string in the format "x / y". + * @param secondString the second string in the format "x / y". + * + * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater + * than the second. + */ + @Override + public int compare(String firstString, String secondString) { + try { + // First String + String[] firstStringParts = firstString.split(DIVIDER_STRING); + int firstStringFirstNumber = MathUtility.parseInt(firstStringParts[0].trim()); + int firstStringSecondNumber = MathUtility.parseInt(firstStringParts[1].trim()); + + // Second String + String[] secondStringParts = secondString.split(DIVIDER_STRING); + int secondStringFirstNumber = MathUtility.parseInt(secondStringParts[0].trim()); + int secondStringSecondNumber = MathUtility.parseInt(secondStringParts[1].trim()); + + // Compare the first numbers + int result = Integer.compare(firstStringFirstNumber, secondStringFirstNumber); + + // If the first numbers are the same, compare the second numbers + if (result == 0) { + result = Integer.compare(firstStringSecondNumber, secondStringSecondNumber); + } + + return result; + // This means the strings are malformed and can't be split into two parts using the divider + } catch (ArrayIndexOutOfBoundsException e) { + logger.error("Error parsing attribute score string: {} or {}", firstString, secondString); + + return 1; // By default, malformed strings go last + } + } +} diff --git a/MekHQ/unittests/mekhq/campaign/personnel/skills/AttributesTest.java b/MekHQ/unittests/mekhq/campaign/personnel/skills/AttributesTest.java index e52d77a15a6..79ac5656dac 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/skills/AttributesTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/skills/AttributesTest.java @@ -27,194 +27,239 @@ */ package mekhq.campaign.personnel.skills; +import static java.lang.Math.min; import static mekhq.campaign.personnel.skills.Attributes.DEFAULT_ATTRIBUTE_SCORE; import static mekhq.campaign.personnel.skills.Attributes.MAXIMUM_ATTRIBUTE_SCORE; import static mekhq.campaign.personnel.skills.Attributes.MINIMUM_ATTRIBUTE_SCORE; +import static mekhq.campaign.personnel.skills.enums.SkillAttribute.BODY; +import static mekhq.campaign.personnel.skills.enums.SkillAttribute.CHARISMA; +import static mekhq.campaign.personnel.skills.enums.SkillAttribute.DEXTERITY; +import static mekhq.campaign.personnel.skills.enums.SkillAttribute.INTELLIGENCE; +import static mekhq.campaign.personnel.skills.enums.SkillAttribute.REFLEXES; +import static mekhq.campaign.personnel.skills.enums.SkillAttribute.STRENGTH; +import static mekhq.campaign.personnel.skills.enums.SkillAttribute.WILLPOWER; import static org.junit.jupiter.api.Assertions.assertEquals; +import mekhq.campaign.personnel.PersonnelOptions; +import mekhq.campaign.personnel.enums.Phenotype; +import mekhq.campaign.personnel.skills.enums.SkillAttribute; import org.junit.jupiter.api.Test; public class AttributesTest { @Test public void testDefaultConstructor() { Attributes attributes = new Attributes(); - assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getStrength()); - assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getBody()); - assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getReflexes()); - assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getDexterity()); - assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getIntelligence()); - assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getWillpower()); - assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getCharisma()); + assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getAttributeScore(STRENGTH)); + assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getAttributeScore(BODY)); + assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getAttributeScore(REFLEXES)); + assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getAttributeScore(DEXTERITY)); + assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getAttributeScore(INTELLIGENCE)); + assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getAttributeScore(WILLPOWER)); + assertEquals(DEFAULT_ATTRIBUTE_SCORE, attributes.getAttributeScore(CHARISMA)); } @Test public void testSetStrength() { - Attributes attributes = new Attributes(); + final Attributes attributes = new Attributes(); + final Phenotype phenotype = Phenotype.GENERAL; + final PersonnelOptions options = new PersonnelOptions(); + + final int attributeCap = phenotype.getAttributeCap(STRENGTH); - attributes.setStrength(MAXIMUM_ATTRIBUTE_SCORE + 1); - assertEquals(MAXIMUM_ATTRIBUTE_SCORE, attributes.getStrength()); + attributes.setAttributeScore(phenotype, options, STRENGTH, MAXIMUM_ATTRIBUTE_SCORE + 1); + assertEquals(attributeCap, attributes.getAttributeScore(STRENGTH)); - attributes.setStrength(MINIMUM_ATTRIBUTE_SCORE - 1); - assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getStrength()); + attributes.setAttributeScore(phenotype, options, STRENGTH, MINIMUM_ATTRIBUTE_SCORE - 1); + assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getAttributeScore(STRENGTH)); for (int i = MINIMUM_ATTRIBUTE_SCORE; i <= MAXIMUM_ATTRIBUTE_SCORE; i++) { - attributes.setStrength(i); - assertEquals(i, attributes.getStrength()); + attributes.setAttributeScore(phenotype, options, STRENGTH, i); + int expected = min(i, attributeCap); + assertEquals(expected, attributes.getAttributeScore(STRENGTH)); } } @Test public void testSetBody() { - Attributes attributes = new Attributes(); + final Attributes attributes = new Attributes(); + final Phenotype phenotype = Phenotype.GENERAL; + final PersonnelOptions options = new PersonnelOptions(); + + final int attributeCap = phenotype.getAttributeCap(BODY); - attributes.setBody(MAXIMUM_ATTRIBUTE_SCORE + 1); - assertEquals(MAXIMUM_ATTRIBUTE_SCORE, attributes.getBody()); + attributes.setAttributeScore(phenotype, options, BODY, MAXIMUM_ATTRIBUTE_SCORE + 1); + assertEquals(attributeCap, attributes.getAttributeScore(BODY)); - attributes.setBody(MINIMUM_ATTRIBUTE_SCORE - 1); - assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getBody()); + attributes.setAttributeScore(phenotype, options, BODY, MINIMUM_ATTRIBUTE_SCORE - 1); + assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getAttributeScore(BODY)); for (int i = MINIMUM_ATTRIBUTE_SCORE; i <= MAXIMUM_ATTRIBUTE_SCORE; i++) { - attributes.setBody(i); - assertEquals(i, attributes.getBody()); + attributes.setAttributeScore(phenotype, options, BODY, i); + int expected = min(i, attributeCap); + assertEquals(expected, attributes.getAttributeScore(BODY)); } } @Test public void testSetReflexes() { - Attributes attributes = new Attributes(); + final Attributes attributes = new Attributes(); + final Phenotype phenotype = Phenotype.GENERAL; + final PersonnelOptions options = new PersonnelOptions(); + + final int attributeCap = phenotype.getAttributeCap(REFLEXES); - attributes.setReflexes(MAXIMUM_ATTRIBUTE_SCORE + 1); - assertEquals(MAXIMUM_ATTRIBUTE_SCORE, attributes.getReflexes()); + attributes.setAttributeScore(phenotype, options, REFLEXES, MAXIMUM_ATTRIBUTE_SCORE + 1); + assertEquals(attributeCap, attributes.getAttributeScore(REFLEXES)); - attributes.setReflexes(MINIMUM_ATTRIBUTE_SCORE - 1); - assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getReflexes()); + attributes.setAttributeScore(phenotype, options, REFLEXES, MINIMUM_ATTRIBUTE_SCORE - 1); + assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getAttributeScore(REFLEXES)); for (int i = MINIMUM_ATTRIBUTE_SCORE; i <= MAXIMUM_ATTRIBUTE_SCORE; i++) { - attributes.setReflexes(i); - assertEquals(i, attributes.getReflexes()); + attributes.setAttributeScore(phenotype, options, REFLEXES, i); + int expected = min(i, attributeCap); + assertEquals(expected, attributes.getAttributeScore(REFLEXES)); } } @Test public void testSetDexterity() { - Attributes attributes = new Attributes(); + final Attributes attributes = new Attributes(); + final Phenotype phenotype = Phenotype.GENERAL; + final PersonnelOptions options = new PersonnelOptions(); - attributes.setDexterity(MAXIMUM_ATTRIBUTE_SCORE + 1); - assertEquals(MAXIMUM_ATTRIBUTE_SCORE, attributes.getDexterity()); + final int attributeCap = phenotype.getAttributeCap(DEXTERITY); - attributes.setDexterity(MINIMUM_ATTRIBUTE_SCORE - 1); - assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getDexterity()); + attributes.setAttributeScore(phenotype, options, DEXTERITY, MAXIMUM_ATTRIBUTE_SCORE + 1); + assertEquals(attributeCap, attributes.getAttributeScore(DEXTERITY)); + + attributes.setAttributeScore(phenotype, options, DEXTERITY, MINIMUM_ATTRIBUTE_SCORE - 1); + assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getAttributeScore(DEXTERITY)); for (int i = MINIMUM_ATTRIBUTE_SCORE; i <= MAXIMUM_ATTRIBUTE_SCORE; i++) { - attributes.setDexterity(i); - assertEquals(i, attributes.getDexterity()); + attributes.setAttributeScore(phenotype, options, DEXTERITY, i); + int expected = min(i, attributeCap); + assertEquals(expected, attributes.getAttributeScore(DEXTERITY)); } } @Test public void testSetIntelligence() { - Attributes attributes = new Attributes(); + final Attributes attributes = new Attributes(); + final Phenotype phenotype = Phenotype.GENERAL; + final PersonnelOptions options = new PersonnelOptions(); - attributes.setIntelligence(MAXIMUM_ATTRIBUTE_SCORE + 1); - assertEquals(MAXIMUM_ATTRIBUTE_SCORE, attributes.getIntelligence()); + final int attributeCap = phenotype.getAttributeCap(INTELLIGENCE); - attributes.setIntelligence(MINIMUM_ATTRIBUTE_SCORE - 1); - assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getIntelligence()); + attributes.setAttributeScore(phenotype, options, INTELLIGENCE, MAXIMUM_ATTRIBUTE_SCORE + 1); + assertEquals(attributeCap, attributes.getAttributeScore(INTELLIGENCE)); + + attributes.setAttributeScore(phenotype, options, INTELLIGENCE, MINIMUM_ATTRIBUTE_SCORE - 1); + assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getAttributeScore(INTELLIGENCE)); for (int i = MINIMUM_ATTRIBUTE_SCORE; i <= MAXIMUM_ATTRIBUTE_SCORE; i++) { - attributes.setIntelligence(i); - assertEquals(i, attributes.getIntelligence()); + attributes.setAttributeScore(phenotype, options, INTELLIGENCE, i); + int expected = min(i, attributeCap); + assertEquals(expected, attributes.getAttributeScore(INTELLIGENCE)); } } @Test public void testSetWillpower() { - Attributes attributes = new Attributes(); + final Attributes attributes = new Attributes(); + final Phenotype phenotype = Phenotype.GENERAL; + final PersonnelOptions options = new PersonnelOptions(); - attributes.setWillpower(MAXIMUM_ATTRIBUTE_SCORE + 1); - assertEquals(MAXIMUM_ATTRIBUTE_SCORE, attributes.getWillpower()); + final int attributeCap = phenotype.getAttributeCap(WILLPOWER); - attributes.setWillpower(MINIMUM_ATTRIBUTE_SCORE - 1); - assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getWillpower()); + attributes.setAttributeScore(phenotype, options, WILLPOWER, MAXIMUM_ATTRIBUTE_SCORE + 1); + assertEquals(phenotype.getAttributeCap(WILLPOWER), attributes.getAttributeScore(WILLPOWER)); + + attributes.setAttributeScore(phenotype, options, WILLPOWER, MINIMUM_ATTRIBUTE_SCORE - 1); + assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getAttributeScore(WILLPOWER)); for (int i = MINIMUM_ATTRIBUTE_SCORE; i <= MAXIMUM_ATTRIBUTE_SCORE; i++) { - attributes.setWillpower(i); - assertEquals(i, attributes.getWillpower()); + attributes.setAttributeScore(phenotype, options, WILLPOWER, i); + int expected = min(i, attributeCap); + assertEquals(expected, attributes.getAttributeScore(WILLPOWER)); } } @Test public void testSetCharisma() { - Attributes attributes = new Attributes(); + final Attributes attributes = new Attributes(); + final Phenotype phenotype = Phenotype.GENERAL; + final PersonnelOptions options = new PersonnelOptions(); + + final int attributeCap = phenotype.getAttributeCap(CHARISMA); - attributes.setCharisma(MAXIMUM_ATTRIBUTE_SCORE + 1); - assertEquals(MAXIMUM_ATTRIBUTE_SCORE, attributes.getCharisma()); + attributes.setAttributeScore(phenotype, options, CHARISMA, MAXIMUM_ATTRIBUTE_SCORE + 1); + assertEquals(phenotype.getAttributeCap(CHARISMA), attributes.getAttributeScore(CHARISMA)); - attributes.setCharisma(MINIMUM_ATTRIBUTE_SCORE - 1); - assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getCharisma()); + attributes.setAttributeScore(phenotype, options, CHARISMA, MINIMUM_ATTRIBUTE_SCORE - 1); + assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getAttributeScore(CHARISMA)); for (int i = MINIMUM_ATTRIBUTE_SCORE; i <= MAXIMUM_ATTRIBUTE_SCORE; i++) { - attributes.setCharisma(i); - assertEquals(i, attributes.getCharisma()); + attributes.setAttributeScore(phenotype, options, CHARISMA, i); + assertEquals(min(i, phenotype.getAttributeCap(CHARISMA)), attributes.getAttributeScore(CHARISMA)); } } @Test public void testChangeAllAttributes_BelowMinimum() { - Attributes attributes = new Attributes(); - attributes.changeAllAttributes(-999); - assertEqualsAllAttributes(MINIMUM_ATTRIBUTE_SCORE, attributes); + final Attributes attributes = new Attributes(); + final Phenotype phenotype = Phenotype.GENERAL; + final PersonnelOptions options = new PersonnelOptions(); + + attributes.changeAllAttributes(phenotype, options, -999); + + for (SkillAttribute attribute : SkillAttribute.values()) { + if (attribute.isNone()) { + continue; + } + + assertEquals(MINIMUM_ATTRIBUTE_SCORE, attributes.getAttributeScore(attribute)); + } } @Test public void testChangeAllAttributes_AboveMaximum() { - Attributes attributes = new Attributes(); - attributes.changeAllAttributes(999); - assertEqualsAllAttributes(MAXIMUM_ATTRIBUTE_SCORE, attributes); - } + final Attributes attributes = new Attributes(); + final Phenotype phenotype = Phenotype.GENERAL; + final PersonnelOptions options = new PersonnelOptions(); - @Test - public void testChangeAllAttributes_AllPossibleValues() { - int minimum = MINIMUM_ATTRIBUTE_SCORE - DEFAULT_ATTRIBUTE_SCORE; - int maximum = MAXIMUM_ATTRIBUTE_SCORE - DEFAULT_ATTRIBUTE_SCORE; + attributes.changeAllAttributes(phenotype, options, 999); - for (int i = minimum; i <= maximum; i++) { - Attributes attributes = new Attributes(); - int expectation = DEFAULT_ATTRIBUTE_SCORE + i; + for (SkillAttribute attribute : SkillAttribute.values()) { + if (attribute.isNone()) { + continue; + } - attributes.changeAllAttributes(i); - assertEqualsAllAttributes(expectation, attributes); + assertEquals(phenotype.getAttributeCap(attribute), attributes.getAttributeScore(attribute)); } } - /** - * Asserts that all attributes of the given {@link Attributes} object are equal to the expected value. - * - *

This utility method performs assertions on the following attributes:

- *
    - *
  • Strength
  • - *
  • Body
  • - *
  • Reflexes
  • - *
  • Dexterity
  • - *
  • Intelligence
  • - *
  • Willpower
  • - *
  • Charisma
  • - *
- *

If any attribute does not match the expected value, an assertion error will be thrown.

- * - * @param expectation the expected value for all attributes. - * @param attributes the {@link Attributes} object whose attributes are being tested. - * - * @throws AssertionError if any attribute does not match the expected value. - * @since 0.50.5 - */ - private static void assertEqualsAllAttributes(int expectation, Attributes attributes) { - assertEquals(expectation, attributes.getStrength()); - assertEquals(expectation, attributes.getBody()); - assertEquals(expectation, attributes.getReflexes()); - assertEquals(expectation, attributes.getDexterity()); - assertEquals(expectation, attributes.getIntelligence()); - assertEquals(expectation, attributes.getWillpower()); - assertEquals(expectation, attributes.getCharisma()); + @Test + public void testChangeAllAttributes_AllPossibleValues() { + final Phenotype phenotype = Phenotype.GENERAL; + final PersonnelOptions options = new PersonnelOptions(); + + for (int i = MINIMUM_ATTRIBUTE_SCORE; i <= MAXIMUM_ATTRIBUTE_SCORE; i++) { + // reset attributes + Attributes attributes = new Attributes(MINIMUM_ATTRIBUTE_SCORE); + + attributes.changeAllAttributes(phenotype, options, i); + + for (SkillAttribute attribute : SkillAttribute.values()) { + if (attribute.isNone()) { + continue; + } + + int newValue = attributes.getAttributeScore(attribute); + int expected = MINIMUM_ATTRIBUTE_SCORE + i; + // Account for attribute caps + expected = min(expected, phenotype.getAttributeCap(attribute)); + assertEquals(expected, newValue); + } + } } } diff --git a/MekHQ/unittests/mekhq/gui/enums/PersonnelTableModelColumnTest.java b/MekHQ/unittests/mekhq/gui/enums/PersonnelTableModelColumnTest.java index 4c1a2181ace..223e5c5f0ec 100644 --- a/MekHQ/unittests/mekhq/gui/enums/PersonnelTableModelColumnTest.java +++ b/MekHQ/unittests/mekhq/gui/enums/PersonnelTableModelColumnTest.java @@ -41,6 +41,7 @@ import megamek.common.util.sorter.NaturalOrderComparator; import mekhq.MekHQ; import mekhq.campaign.Campaign; +import mekhq.gui.sorter.AttributeScoreSorter; import mekhq.gui.sorter.BonusSorter; import mekhq.gui.sorter.DateStringComparator; import mekhq.gui.sorter.FormattedNumberSorter; @@ -848,77 +849,58 @@ public void testGetComparator() { final Campaign mockCampaign = mock(Campaign.class); for (final PersonnelTableModelColumn personnelTableModelColumn : columns) { switch (personnelTableModelColumn) { - case RANK: - assertInstanceOf(PersonRankStringSorter.class, - personnelTableModelColumn.getComparator(mockCampaign)); - break; - case AGE: - case BIRTHDAY: - case RECRUITMENT_DATE: - case LAST_RANK_CHANGE_DATE: - case DUE_DATE: - case RETIREMENT_DATE: - case DEATH_DATE: - assertInstanceOf(DateStringComparator.class, personnelTableModelColumn.getComparator(mockCampaign)); - break; - case SKILL_LEVEL: - assertInstanceOf(LevelSorter.class, personnelTableModelColumn.getComparator(mockCampaign)); - break; - case MEK: - case GROUND_VEHICLE: - case NAVAL_VEHICLE: - case VTOL: - case AEROSPACE: - case CONVENTIONAL_AIRCRAFT: - case VESSEL: - case BATTLE_ARMOUR: - case SMALL_ARMS: - case ANTI_MEK: - case ARTILLERY: - case TACTICS: - case STRATEGY: - case LEADERSHIP: - case TECH_MEK: - case TECH_AERO: - case TECH_MECHANIC: - case TECH_BA: - case TECH_VESSEL: - case MEDICAL: - case ADMINISTRATION: - case NEGOTIATION: - case SCROUNGE: - assertInstanceOf(BonusSorter.class, personnelTableModelColumn.getComparator(mockCampaign)); - break; - case INJURIES: - case KILLS: - case XP: - case TOUGHNESS: - case CONNECTIONS: - case WEALTH: - case REPUTATION: - case UNLUCKY: - case EDGE: - case SPA_COUNT: - case IMPLANT_COUNT: - case LOYALTY: - case REASONING: - case STRENGTH: - case BODY: - case REFLEXES: - case DEXTERITY: - case INTELLIGENCE: - case WILLPOWER: - case CHARISMA: - assertInstanceOf(IntegerStringSorter.class, personnelTableModelColumn.getComparator(mockCampaign)); - break; - case SALARY: - assertInstanceOf(FormattedNumberSorter.class, - personnelTableModelColumn.getComparator(mockCampaign)); - break; - default: - assertInstanceOf(NaturalOrderComparator.class, - personnelTableModelColumn.getComparator(mockCampaign)); - break; + case RANK -> assertInstanceOf(PersonRankStringSorter.class, + personnelTableModelColumn.getComparator(mockCampaign)); + case AGE, BIRTHDAY, RECRUITMENT_DATE, LAST_RANK_CHANGE_DATE, DUE_DATE, RETIREMENT_DATE, DEATH_DATE -> + assertInstanceOf(DateStringComparator.class, + personnelTableModelColumn.getComparator(mockCampaign)); + case SKILL_LEVEL -> + assertInstanceOf(LevelSorter.class, personnelTableModelColumn.getComparator(mockCampaign)); + case MEK, + GROUND_VEHICLE, + NAVAL_VEHICLE, + VTOL, + AEROSPACE, + CONVENTIONAL_AIRCRAFT, + VESSEL, + BATTLE_ARMOUR, + SMALL_ARMS, + ANTI_MEK, + ARTILLERY, + TACTICS, + STRATEGY, + LEADERSHIP, + TECH_MEK, + TECH_AERO, + TECH_MECHANIC, + TECH_BA, + TECH_VESSEL, + MEDICAL, + ADMINISTRATION, + NEGOTIATION, + SCROUNGE -> + assertInstanceOf(BonusSorter.class, personnelTableModelColumn.getComparator(mockCampaign)); + case INJURIES, + KILLS, + XP, + TOUGHNESS, + CONNECTIONS, + WEALTH, + REPUTATION, + UNLUCKY, + EDGE, + SPA_COUNT, + IMPLANT_COUNT, + LOYALTY, + REASONING -> assertInstanceOf(IntegerStringSorter.class, + personnelTableModelColumn.getComparator(mockCampaign)); + case STRENGTH, BODY, REFLEXES, DEXTERITY, INTELLIGENCE, WILLPOWER, CHARISMA -> assertInstanceOf( + AttributeScoreSorter.class, + personnelTableModelColumn.getComparator(mockCampaign)); + case SALARY -> assertInstanceOf(FormattedNumberSorter.class, + personnelTableModelColumn.getComparator(mockCampaign)); + default -> assertInstanceOf(NaturalOrderComparator.class, + personnelTableModelColumn.getComparator(mockCampaign)); } } } From f62c51000b4002d8e3314ab4adbe7edc57d08c55 Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Fri, 11 Apr 2025 19:38:56 -0500 Subject: [PATCH 09/10] - Updated `getAttributeScore` logic in `Person.java` to account for SPAs. --- .../src/mekhq/campaign/personnel/Person.java | 38 ++++++++++++++++++- .../campaign/personnel/skills/Attributes.java | 1 + 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/Person.java b/MekHQ/src/mekhq/campaign/personnel/Person.java index 3cf70db31b5..bb064c22040 100644 --- a/MekHQ/src/mekhq/campaign/personnel/Person.java +++ b/MekHQ/src/mekhq/campaign/personnel/Person.java @@ -5235,7 +5235,43 @@ public int getAttributeScore(final SkillAttribute attribute) { return DEFAULT_ATTRIBUTE_SCORE; } - return atowAttributes.getAttributeScore(attribute); + boolean hasFreakishStrength = options.booleanOption(MUTATION_FREAKISH_STRENGTH); + boolean hasExoticAppearance = options.booleanOption(MUTATION_EXOTIC_APPEARANCE); + boolean hasFacialHair = options.booleanOption(MUTATION_FACIAL_HAIR); + boolean hasSeriousDisfigurement = options.booleanOption(MUTATION_SERIOUS_DISFIGUREMENT); + boolean isCatGirl = options.booleanOption(MUTATION_CAT_GIRL); + boolean isCatGirlUnofficial = options.booleanOption(MUTATION_CAT_GIRL_UNOFFICIAL); + + return switch (attribute) { + case NONE -> 0; + case STRENGTH -> { + int attributeScore = atowAttributes.getAttributeScore(attribute); + if (hasFreakishStrength) { + attributeScore += 2; + } + yield min(attributeScore, MAXIMUM_ATTRIBUTE_SCORE); + } + case BODY, REFLEXES, DEXTERITY, INTELLIGENCE, WILLPOWER -> atowAttributes.getAttributeScore(attribute); + case CHARISMA -> { + int attributeScore = atowAttributes.getAttributeScore(attribute); + if (hasExoticAppearance) { + attributeScore++; + } + if (hasFacialHair) { + attributeScore--; + } + if (hasSeriousDisfigurement) { + attributeScore -= 3; + } + if (isCatGirl) { + attributeScore -= 3; + } + if (isCatGirlUnofficial) { + attributeScore++; + } + yield clamp(attributeScore, MINIMUM_ATTRIBUTE_SCORE, MAXIMUM_ATTRIBUTE_SCORE); + } + }; } /** diff --git a/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java b/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java index 7bb56f99bce..2eba96e587e 100644 --- a/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java +++ b/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java @@ -35,6 +35,7 @@ import static mekhq.campaign.personnel.PersonnelOptions.EXCEPTIONAL_ATTRIBUTE_REFLEXES; import static mekhq.campaign.personnel.PersonnelOptions.EXCEPTIONAL_ATTRIBUTE_STRENGTH; import static mekhq.campaign.personnel.PersonnelOptions.EXCEPTIONAL_ATTRIBUTE_WILLPOWER; +import static mekhq.campaign.personnel.PersonnelOptions.MUTATION_FREAKISH_STRENGTH; import java.io.PrintWriter; From 95732d39b1ddacde6f2e0133d6e141ee5d5d514c Mon Sep 17 00:00:00 2001 From: IllianiCBT Date: Fri, 11 Apr 2025 19:41:04 -0500 Subject: [PATCH 10/10] - Fixed javadocs --- .../campaign/personnel/skills/Attributes.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java b/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java index 2eba96e587e..f51f1627e82 100644 --- a/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java +++ b/MekHQ/src/mekhq/campaign/personnel/skills/Attributes.java @@ -352,7 +352,7 @@ public int getStrength() { } /** - * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. + * @deprecated use {@link #setAttributeScore(Phenotype, PersonnelOptions, SkillAttribute, int)} instead. */ @Deprecated(since = "0.50.5", forRemoval = true) public void setStrength(int strength) { @@ -368,7 +368,7 @@ public int getBody() { } /** - * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. + * @deprecated use {@link #setAttributeScore(Phenotype, PersonnelOptions, SkillAttribute, int)} instead. */ @Deprecated(since = "0.50.5", forRemoval = true) public void setBody(int body) { @@ -384,7 +384,7 @@ public int getReflexes() { } /** - * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. + * @deprecated use {@link #setAttributeScore(Phenotype, PersonnelOptions, SkillAttribute, int)} instead. */ @Deprecated(since = "0.50.5", forRemoval = true) public void setReflexes(int reflexes) { @@ -400,7 +400,7 @@ public int getDexterity() { } /** - * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. + * @deprecated use {@link #setAttributeScore(Phenotype, PersonnelOptions, SkillAttribute, int)} instead. */ @Deprecated(since = "0.50.5", forRemoval = true) public void setDexterity(int dexterity) { @@ -416,7 +416,7 @@ public int getIntelligence() { } /** - * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. + * @deprecated use {@link #setAttributeScore(Phenotype, PersonnelOptions, SkillAttribute, int)} instead. */ @Deprecated(since = "0.50.5", forRemoval = true) public void setIntelligence(int intelligence) { @@ -432,7 +432,7 @@ public int getWillpower() { } /** - * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. + * @deprecated use {@link #setAttributeScore(Phenotype, PersonnelOptions, SkillAttribute, int)} instead. */ @Deprecated(since = "0.50.5", forRemoval = true) public void setWillpower(int willpower) { @@ -448,7 +448,7 @@ public int getCharisma() { } /** - * @deprecated use {@link #setAttributeScore(SkillAttribute, int)} instead. + * @deprecated use {@link #setAttributeScore(Phenotype, PersonnelOptions, SkillAttribute, int)} instead. */ @Deprecated(since = "0.50.5", forRemoval = true) public void setCharisma(int charisma) {