diff --git a/MekHQ/src/mekhq/campaign/personnel/Person.java b/MekHQ/src/mekhq/campaign/personnel/Person.java index 4eaece194f7..f8aee817b73 100644 --- a/MekHQ/src/mekhq/campaign/personnel/Person.java +++ b/MekHQ/src/mekhq/campaign/personnel/Person.java @@ -7057,6 +7057,28 @@ public int getAttributeCap(final SkillAttribute attribute) { return atowAttributes.getAttributeCap(phenotype, options, attribute); } + /** + * Retrieves the modifier value for a specified skill attribute. + * + * @param attribute the skill attribute for which the modifier is to be calculated; + * if the attribute is null or represents "none", a warning is logged and the method returns 0 + * @return the calculated modifier value for the provided skill attribute, or 0 if the attribute is null or "none" + * + * @author Illiani + * @since 0.51.00 + */ + public int getAttributeModifier(final SkillAttribute attribute) { + if (attribute == null || attribute.isNone()) { + LOGGER.warn("(getAttributeModifier) SkillAttribute is null or NONE."); + return 0; + } + + return atowAttributes.getAttributeModifier(attribute, + getActiveInjuryEffects(), + options, + ageForAttributeModifiers); + } + /** * Sets the character's {@link Attributes} object which contains their ATOW Attribute scores. * diff --git a/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java b/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java index 6459b9174be..aa926361fe2 100644 --- a/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java +++ b/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java @@ -85,6 +85,7 @@ import mekhq.gui.sorter.PersonRankStringSorter; import mekhq.gui.sorter.ReasoningSorter; import mekhq.utilities.ReportingUtilities; +import org.jspecify.annotations.NonNull; public enum PersonnelTableModelColumn { // region Enum Declarations @@ -619,10 +620,6 @@ public boolean isPersonality() { public String getCellValue(final Campaign campaign, final PersonnelMarket personnelMarket, final Person person, final boolean loadAssignmentFromMarket, final boolean groupByUnit) { - // We define these here, as they're used in multiple cases - int currentAttributeValue; - int attributeCap; - String sign; final boolean isClanCampaign = campaign.isClanCampaign(); @@ -1122,42 +1119,49 @@ public String getCellValue(final Campaign campaign, final PersonnelMarket person Reasoning reasoning = person.getReasoning(); return reasoning.getLabel(); case STRENGTH: - currentAttributeValue = person.getAttributeScore(SkillAttribute.STRENGTH); - attributeCap = person.getAttributeCap(SkillAttribute.STRENGTH); - return currentAttributeValue + " / " + attributeCap; + return getAttributeScoreDisplay(person, SkillAttribute.STRENGTH); case BODY: - currentAttributeValue = person.getAttributeScore(SkillAttribute.BODY); - attributeCap = person.getAttributeCap(SkillAttribute.BODY); - return currentAttributeValue + " / " + attributeCap; + return getAttributeScoreDisplay(person, SkillAttribute.BODY); case REFLEXES: - currentAttributeValue = person.getAttributeScore(SkillAttribute.REFLEXES); - attributeCap = person.getAttributeCap(SkillAttribute.REFLEXES); - return currentAttributeValue + " / " + attributeCap; + return getAttributeScoreDisplay(person, SkillAttribute.REFLEXES); case DEXTERITY: - currentAttributeValue = person.getAttributeScore(SkillAttribute.DEXTERITY); - attributeCap = person.getAttributeCap(SkillAttribute.DEXTERITY); - return currentAttributeValue + " / " + attributeCap; + return getAttributeScoreDisplay(person, SkillAttribute.DEXTERITY); case INTELLIGENCE: - currentAttributeValue = person.getAttributeScore(SkillAttribute.INTELLIGENCE); - attributeCap = person.getAttributeCap(SkillAttribute.INTELLIGENCE); - return currentAttributeValue + " / " + attributeCap; + return getAttributeScoreDisplay(person, SkillAttribute.INTELLIGENCE); case WILLPOWER: - currentAttributeValue = person.getAttributeScore(SkillAttribute.WILLPOWER); - attributeCap = person.getAttributeCap(SkillAttribute.WILLPOWER); - return currentAttributeValue + " / " + attributeCap; + return getAttributeScoreDisplay(person, SkillAttribute.WILLPOWER); case CHARISMA: - currentAttributeValue = person.getAttributeScore(SkillAttribute.CHARISMA); - attributeCap = person.getAttributeCap(SkillAttribute.CHARISMA); - return currentAttributeValue + " / " + attributeCap; + return getAttributeScoreDisplay(person, SkillAttribute.CHARISMA); case EDGE: - currentAttributeValue = person.getAttributeScore(SkillAttribute.EDGE); - attributeCap = person.getAttributeCap(SkillAttribute.EDGE); + int currentAttributeValue = person.getAttributeScore(SkillAttribute.EDGE); + int attributeCap = person.getAttributeCap(SkillAttribute.EDGE); return currentAttributeValue + " / " + attributeCap; default: return "UNIMPLEMENTED"; } } + /** + * Constructs a displayable string representation of a person's skill attribute, including their current score, + * maximum possible score (cap), and attribute modifier. + * + * @param person the person whose attribute scores are being represented + * @param attribute the specific skill attribute being evaluated + * @return a string in the format "currentScore / attributeCap (+/- modifier)" + * + * @author Illiani + * @since 0.51.00 + */ + private static @NonNull String getAttributeScoreDisplay(Person person, SkillAttribute attribute) { + int currentAttributeValue = person.getAttributeScore(attribute); + int attributeCap = person.getAttributeCap(attribute); + + int attributeModifier = person.getAttributeModifier(attribute); + String sign = attributeModifier >= 0 ? "+" : ""; + + return currentAttributeValue + " / " + attributeCap + " (" + sign + attributeModifier + ")"; + } + public @Nullable String getDisplayText(final Campaign campaign, final Person person) { if (this == PersonnelTableModelColumn.AGE) { return Integer.toString(person.getAge(campaign.getLocalDate()));