Skip to content

Commit 373414f

Browse files
authored
Merge pull request #6575 from IllianiBird/agingUpdate
Updated Aging Effects to Include Clan Reputation Modifiers & Glass Jaw Gain
2 parents 5cdae07 + f272e44 commit 373414f

File tree

12 files changed

+263
-45
lines changed

12 files changed

+263
-45
lines changed

MekHQ/src/mekhq/campaign/Campaign.java

+19-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import static mekhq.campaign.personnel.lifeEvents.FreedomDayAnnouncement.isFreedomDay;
5555
import static mekhq.campaign.personnel.lifeEvents.NewYearsDayAnnouncement.isNewYear;
5656
import static mekhq.campaign.personnel.lifeEvents.WinterHolidayAnnouncement.isWinterHolidayMajorDay;
57+
import static mekhq.campaign.personnel.skills.Aging.applyAgingSPA;
5758
import static mekhq.campaign.personnel.skills.Aging.updateAllSkillAgeModifiers;
5859
import static mekhq.campaign.personnel.turnoverAndRetention.Fatigue.areFieldKitchensWithinCapacity;
5960
import static mekhq.campaign.personnel.turnoverAndRetention.Fatigue.checkFieldKitchenCapacity;
@@ -5096,6 +5097,7 @@ private void processWeeklyRelationshipEvents(Person person) {
50965097
private void processAnniversaries(Person person) {
50975098
LocalDate birthday = person.getBirthday(getGameYear());
50985099
boolean isBirthday = birthday != null && birthday.equals(currentDay);
5100+
int age = person.getAge(currentDay);
50995101

51005102
if ((person.getRank().isOfficer()) || (!campaignOptions.isAnnounceOfficersOnly())) {
51015103
if (isBirthday && campaignOptions.isAnnounceBirthdays()) {
@@ -5132,14 +5134,15 @@ private void processAnniversaries(Person person) {
51325134
}
51335135

51345136
if (campaignOptions.isShowLifeEventDialogComingOfAge()) {
5135-
if ((person.getAge(currentDay) == 16) && (isBirthday)) {
5137+
if ((age == 16) && (isBirthday)) {
51365138
new ComingOfAgeAnnouncement(this, person);
51375139
}
51385140
}
51395141

51405142
if (campaignOptions.isUseAgeEffects() && isBirthday) {
51415143
// This is where we update all the aging modifiers for the character.
51425144
updateAllSkillAgeModifiers(currentDay, person);
5145+
applyAgingSPA(age, person);
51435146
}
51445147

51455148
if (campaignOptions.isRewardComingOfAgeAbilities() && isBirthday && (person.getAge(currentDay) == 16)) {
@@ -6114,6 +6117,21 @@ public Faction getFaction() {
61146117
return faction;
61156118
}
61166119

6120+
/**
6121+
* Determines whether the current campaign is a clan campaign.
6122+
*
6123+
* <p>This method checks if the faction associated with the campaign is a clan, returning {@code true}
6124+
* if it is, and {@code false} otherwise.</p>
6125+
*
6126+
* @return {@code true} if the campaign belongs to a clan faction, {@code false} otherwise.
6127+
*
6128+
* @author Illiani
6129+
* @since 0.50.05
6130+
*/
6131+
public boolean isClanCampaign() {
6132+
return faction.isClan();
6133+
}
6134+
61176135
public void setFaction(final Faction faction) {
61186136
setFactionDirect(faction);
61196137
updateTechFactionCode();

MekHQ/src/mekhq/campaign/mission/resupplyAndCaches/Resupply.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,11 @@ private void calculateNegotiationSkill() {
778778
Skill skill = negotiator.getSkill(SkillType.S_NEG);
779779

780780
if (skill != null) {
781-
int skillLevel = skill.getFinalSkillValue(negotiator.getOptions(), negotiator.getReputation());
781+
int reputation = negotiator.getAdjustedReputation(campaign.getCampaignOptions().isUseAgeEffects(),
782+
campaign.isClanCampaign(),
783+
campaign.getLocalDate(),
784+
negotiator.getRankLevel());
785+
int skillLevel = skill.getFinalSkillValue(negotiator.getOptions(), reputation);
782786
negotiatorSkill = skill.getType().getExperienceLevel(skillLevel);
783787
}
784788
}

MekHQ/src/mekhq/campaign/personnel/Person.java

+39
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import static mekhq.campaign.personnel.skills.Attributes.DEFAULT_ATTRIBUTE_SCORE;
4545
import static mekhq.campaign.personnel.skills.Attributes.MAXIMUM_ATTRIBUTE_SCORE;
4646
import static mekhq.campaign.personnel.skills.Attributes.MINIMUM_ATTRIBUTE_SCORE;
47+
import static mekhq.campaign.personnel.skills.Aging.getReputationAgeModifier;
4748
import static mekhq.campaign.personnel.skills.SkillType.S_ADMIN;
4849

4950
import java.io.PrintWriter;
@@ -5285,10 +5286,48 @@ public void setHasPerformedExtremeExpenditure(final boolean hasPerformedExtremeE
52855286
this.hasPerformedExtremeExpenditure = hasPerformedExtremeExpenditure;
52865287
}
52875288

5289+
/**
5290+
* Retrieves the raw reputation value of the character.
5291+
*
5292+
* <p>This method returns the unadjusted reputation value associated with the character.</p>
5293+
*
5294+
* <p><b>Usage:</b> If aging effects are enabled, you likely want to use
5295+
* {@link #getAdjustedReputation(boolean, boolean, LocalDate, int)} instead.</p>
5296+
*
5297+
* @return The raw reputation value.
5298+
*/
52885299
public int getReputation() {
52895300
return reputation;
52905301
}
52915302

5303+
/**
5304+
* Calculates the adjusted reputation value for the character based on aging effects, the current campaign type,
5305+
* date, and rank.
5306+
*
5307+
* <p>This method computes the character's reputation by applying age-based modifiers, which depend on factors such
5308+
* as whether aging effects are enabled, whether the campaign is clan-specific, the character's bloodname status,
5309+
* and their rank in the clan hierarchy. If aging effects are disabled, the reputation remains unchanged.</p>
5310+
*
5311+
* <p><b>Usage:</b> If aging effects are disabled, the result will be equivalent to the base reputation value
5312+
* provided by {@link #getReputation()}.</p>
5313+
*
5314+
* @param isUseAgingEffects Indicates whether aging effects should be applied to the reputation calculation.
5315+
* @param isClanCampaign Indicates whether the current campaign is specific to a clan.
5316+
* @param today The current date used to calculate the character's age.
5317+
* @param rankIndex The rank index of the character, which can adjust the reputation modifier in clan-based
5318+
* campaigns.
5319+
*
5320+
* @return The adjusted reputation value, accounting for factors like age, clan campaign status, bloodname
5321+
* possession, and rank. If aging effects are disabled, the base reputation value is returned.
5322+
*/
5323+
public int getAdjustedReputation(boolean isUseAgingEffects, boolean isClanCampaign, LocalDate today,
5324+
int rankIndex) {
5325+
return reputation +
5326+
(isUseAgingEffects ?
5327+
getReputationAgeModifier(getAge(today), isClanCampaign, !bloodname.isBlank(), rankIndex) :
5328+
0);
5329+
}
5330+
52925331
public void setReputation(final int reputation) {
52935332
this.reputation = clamp(reputation, MINIMUM_REPUTATION, MAXIMUM_REPUTATION);
52945333
}

MekHQ/src/mekhq/campaign/personnel/skills/Aging.java

+114-1
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,23 @@
2727
*/
2828
package mekhq.campaign.personnel.skills;
2929

30+
import static megamek.common.options.PilotOptions.LVL3_ADVANTAGES;
31+
import static mekhq.campaign.personnel.PersonnelOptions.ATOW_FAST_LEARNER;
32+
import static mekhq.campaign.personnel.PersonnelOptions.ATOW_TOUGHNESS;
33+
import static mekhq.campaign.personnel.PersonnelOptions.FLAW_GLASS_JAW;
34+
import static mekhq.campaign.personnel.PersonnelOptions.FLAW_SLOW_LEARNER;
35+
import static mekhq.campaign.personnel.skills.enums.AgingMilestone.CLAN_REPUTATION_MULTIPLIER;
3036
import static mekhq.campaign.personnel.skills.enums.AgingMilestone.NONE;
37+
import static mekhq.campaign.personnel.skills.enums.AgingMilestone.STAR_CAPTAIN_RANK_INDEX;
38+
import static mekhq.campaign.personnel.skills.enums.AgingMilestone.STAR_CAPTAIN_REPUTATION_MULTIPLIER;
39+
import static mekhq.campaign.personnel.skills.enums.AgingMilestone.STAR_COLONEL_REPUTATION_MULTIPLIER;
3140
import static mekhq.campaign.personnel.skills.enums.AgingMilestone.TWENTY_FIVE;
3241
import static mekhq.campaign.personnel.skills.enums.SkillAttribute.NO_SKILL_ATTRIBUTE;
3342

3443
import java.time.LocalDate;
3544

3645
import mekhq.campaign.personnel.Person;
46+
import mekhq.campaign.personnel.PersonnelOptions;
3747
import mekhq.campaign.personnel.skills.enums.AgingMilestone;
3848
import mekhq.campaign.personnel.skills.enums.SkillAttribute;
3949

@@ -89,6 +99,17 @@ public static void updateAllSkillAgeModifiers(LocalDate today, Person person) {
8999
}
90100
}
91101

102+
/**
103+
* Resets all age-related modifiers for the skills of a given person to zero.
104+
*
105+
* <p>This method iterates through all skills in the {@code SkillType.skillList} and, for each skill that
106+
* the person possesses, sets its aging modifier to zero. Skills that the person does not have are ignored.</p>
107+
*
108+
* @param person The person whose skill age modifiers will be cleared.
109+
*
110+
* @author Illiani
111+
* @since 0.50.05
112+
*/
92113
public static void clearAllAgeModifiers(Person person) {
93114
for (String skillName : SkillType.skillList) {
94115
boolean hasSkill = person.hasSkill(skillName);
@@ -156,7 +177,7 @@ public static int getAgeModifier(int characterAge, SkillAttribute firstAttribute
156177
* 0} if no valid combination or milestone exists
157178
*/
158179
public static int getAgeModifier(AgingMilestone milestone, SkillAttribute firstAttribute,
159-
SkillAttribute secondAttribute) {
180+
SkillAttribute secondAttribute) {
160181
// If no milestone applies, return no modifier
161182
if (milestone == NONE) {
162183
return 0;
@@ -180,6 +201,98 @@ public static int getAgeModifier(AgingMilestone milestone, SkillAttribute firstA
180201
return applyAgingModifier((firstModifier + secondModifier) / 2);
181202
}
182203

204+
/**
205+
* Calculates the reputation age modifier for a character based on their age, clan affiliation, bloodname status,
206+
* and military rank.
207+
*
208+
* <p>This method determines a character's reputation age modifier by evaluating their age against a predefined
209+
* aging milestone, their clan affiliation, their possession of a bloodname, and their rank in the clan hierarchy.
210+
* If the character meets specific conditions, such as holding a high enough rank or possessing a bloodname, the
211+
* reputation multiplier is adjusted. The final result is scaled by a clan-specific reputation multiplier.</p>
212+
*
213+
* @param characterAge The age of the character for which the reputation modifier is being calculated.
214+
* @param isClan Indicates whether the character is part of a clan. If {@code false}, the method returns 0.
215+
* @param hasBloodname Indicates whether the character possesses a bloodname, which can decrease the reputation
216+
* multiplier under certain conditions.
217+
* @param rankIndex The rank index of the character, used to determine if they meet rank-specific milestone
218+
* conditions for reputation adjustment.
219+
*
220+
* @return The calculated reputation age modifier. Returns 0 if the character is not a clan member.
221+
*
222+
* @author Illiani
223+
* @since 0.50.05
224+
*/
225+
public static int getReputationAgeModifier(int characterAge, boolean isClan, boolean hasBloodname, int rankIndex) {
226+
if (!isClan) {
227+
return 0;
228+
}
229+
230+
AgingMilestone milestone = getMilestone(characterAge);
231+
232+
int reputationMultiplier = milestone.getReputation();
233+
boolean hasHitRankTarget = false;
234+
235+
if (reputationMultiplier == STAR_CAPTAIN_REPUTATION_MULTIPLIER && rankIndex >= STAR_CAPTAIN_RANK_INDEX) {
236+
hasHitRankTarget = true;
237+
}
238+
239+
if (reputationMultiplier == STAR_COLONEL_REPUTATION_MULTIPLIER && rankIndex >= STAR_CAPTAIN_RANK_INDEX) {
240+
hasHitRankTarget = true;
241+
}
242+
243+
if (hasHitRankTarget || hasBloodname) {
244+
reputationMultiplier--;
245+
}
246+
247+
return reputationMultiplier * CLAN_REPUTATION_MULTIPLIER;
248+
}
249+
250+
/**
251+
* Applies age-related special abilities or flaws to a given person based on their age.
252+
*
253+
* <p>This method evaluates the character's age against predefined aging milestones, and if the age matches
254+
* a milestone, specific effects such as applying flaws or adjusting abilities are triggered. For example, it may
255+
* apply the "Glass Jaw" flaw or interact with existing abilities like "Toughness".</p>
256+
*
257+
* @param characterAge The age of the character, used to determine applicable aging milestones and effects.
258+
* @param person The person to whom the aging-related effects will be applied.
259+
*
260+
* @author Illiani
261+
* @since 0.50.05
262+
*/
263+
public static void applyAgingSPA(int characterAge, Person person) {
264+
PersonnelOptions options = person.getOptions();
265+
for (AgingMilestone milestone : AgingMilestone.values()) {
266+
if (characterAge == milestone.getMilestone()) {
267+
// Glass Jaw
268+
if (milestone.isGlassJaw()) {
269+
boolean hasGlassJaw = options.booleanOption(FLAW_GLASS_JAW);
270+
boolean hasToughness = options.booleanOption(ATOW_TOUGHNESS);
271+
272+
if (hasToughness) {
273+
person.getOptions().getOption(ATOW_TOUGHNESS).setValue(false);
274+
} else if (!hasGlassJaw) {
275+
options.acquireAbility(LVL3_ADVANTAGES, FLAW_GLASS_JAW, true);
276+
}
277+
}
278+
279+
// Slow Learner
280+
if (milestone.isSlowLearner()) {
281+
boolean hasSlowLearner = options.booleanOption(FLAW_SLOW_LEARNER);
282+
boolean hasFastLearner = options.booleanOption(ATOW_FAST_LEARNER);
283+
284+
if (hasFastLearner) {
285+
person.getOptions().getOption(ATOW_FAST_LEARNER).setValue(false);
286+
} else if (!hasSlowLearner) {
287+
options.acquireAbility(LVL3_ADVANTAGES, FLAW_SLOW_LEARNER, true);
288+
}
289+
}
290+
291+
break;
292+
}
293+
}
294+
}
295+
183296
/**
184297
* Determines the appropriate {@link AgingMilestone} for a given character's age.
185298
*

MekHQ/src/mekhq/campaign/personnel/skills/enums/AgingMilestone.java

+15-9
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,23 @@
3535
public enum AgingMilestone {
3636
NONE(0, 25, 0, 0, 0, 0, 0, 0, 0, 0, false, false),
3737
TWENTY_FIVE(25, 31, 50, 50, 0, 50, 50, 50, 50, 0, false, false),
38-
THIRTY_ONE(31, 14, 50, 50, 0, 50, 50, 50, 0, -150, false, false),
39-
FORTY_ONE(41, 51, 0, 0, -50, 0, 0, 0, 0, 0, false, false),
40-
FIFTY_ONE(51, 61, 0, -100, 0, -100, 0, 0, -50, -300, false, false),
41-
SIXTY_ONE(61, 71, -100, -100, -100, 0, 50, 0, -50, 0, true, false),
42-
SEVENTY_ONE(71, 81, -100, -125, 0, -100, 0, -50, -75, 0, true, true),
43-
EIGHTY_ONE(81, 91, -150, -150, -100, -100, -100, -50, -100, 0, true, true),
44-
NINETY_ONE(91, 101, -150, -175, -150, -125, -150, -100, -100, 0, true, true),
45-
ONE_HUNDRED_ONE(101, MAX_VALUE, -200, -200, -200, -150, -200, -100, -1500, 0, true, true);
38+
THIRTY_ONE(31, 14, 50, 50, 0, 50, 50, 50, 0, 1, false, false),
39+
FORTY_ONE(41, 51, 0, 0, -50, 0, 0, 0, 0, 1, false, false),
40+
FIFTY_ONE(51, 61, 0, -100, 0, -100, 0, 0, -50, 2, false, false),
41+
SIXTY_ONE(61, 71, -100, -100, -100, 0, 50, 0, -50, 2, true, false),
42+
SEVENTY_ONE(71, 81, -100, -125, 0, -100, 0, -50, -75, 2, true, true),
43+
EIGHTY_ONE(81, 91, -150, -150, -100, -100, -100, -50, -100, 2, true, true),
44+
NINETY_ONE(91, 101, -150, -175, -150, -125, -150, -100, -100, 2, true, true),
45+
ONE_HUNDRED_ONE(101, MAX_VALUE, -200, -200, -200, -150, -200, -100, -1500, 2, true, true);
4646

4747
private static final MMLogger logger = MMLogger.create(AgingMilestone.class);
4848

49+
public static final int CLAN_REPUTATION_MULTIPLIER = 150;
50+
public static final int STAR_CAPTAIN_RANK_INDEX = 34;
51+
public static final int STAR_CAPTAIN_REPUTATION_MULTIPLIER = 1;
52+
public static final int STAR_COLONEL_RANK_INDEX = 38;
53+
public static final int STAR_COLONEL_REPUTATION_MULTIPLIER = 2;
54+
4955
// Attributes
5056
private final int milestone;
5157
private final int maximumAge;
@@ -100,7 +106,7 @@ public enum AgingMilestone {
100106

101107
// Constructor
102108
AgingMilestone(int milestone, int maximumAge, int strength, int body, int dexterity, int reflexes, int intelligence,
103-
int willpower, int charisma, int reputation, boolean slowLearner, boolean glassJaw) {
109+
int willpower, int charisma, int reputation, boolean slowLearner, boolean glassJaw) {
104110
this.milestone = milestone;
105111
this.maximumAge = maximumAge;
106112
this.strength = strength;

MekHQ/src/mekhq/campaign/rating/CamOpsReputation/CommandRating.java

+12-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import static megamek.common.options.OptionsConstants.ATOW_COMBAT_SENSE;
3333
import static mekhq.campaign.randomEvents.personalities.PersonalityController.getPersonalityValue;
3434

35+
import java.time.LocalDate;
3536
import java.util.HashMap;
3637
import java.util.Map;
3738
import java.util.stream.Collectors;
@@ -70,7 +71,11 @@ protected static Map<String, Integer> calculateCommanderRating(Campaign campaign
7071
commandRating.put("strategy", getSkillValue(commander, SkillType.S_STRATEGY));
7172
commandRating.put("negotiation", getSkillValue(commander, SkillType.S_NEG));
7273

73-
commandRating.put("traits", getATOWTraitValues(commander));
74+
commandRating.put("traits",
75+
getATOWTraitValues(commander,
76+
campaign.getCampaignOptions().isUseAgeEffects(),
77+
campaign.isClanCampaign(),
78+
campaign.getLocalDate()));
7479

7580
int personalityValue = 0;
7681
CampaignOptions campaignOptions = campaign.getCampaignOptions();
@@ -116,7 +121,8 @@ protected static Map<String, Integer> calculateCommanderRating(Campaign campaign
116121
*
117122
* @return The calculated trait score for the commander, with a minimum value of 1.
118123
*/
119-
private static int getATOWTraitValues(Person commander) {
124+
private static int getATOWTraitValues(Person commander, boolean isUseAgingEffects, boolean isClanCampaign,
125+
LocalDate today) {
120126
if (commander == null) {
121127
return 0;
122128
}
@@ -131,7 +137,10 @@ private static int getATOWTraitValues(Person commander) {
131137
traitScore += commander.getWealth() >= 7 ? 1 : 0;
132138

133139
// Reputation
134-
int reputation = commander.getReputation();
140+
int reputation = commander.getAdjustedReputation(isUseAgingEffects,
141+
isClanCampaign,
142+
today,
143+
commander.getRankLevel());
135144
if (reputation < 0) {
136145
traitScore -= 1;
137146
} else if (reputation > 0) {

MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -610,16 +610,14 @@ public void actionPerformed(ActionEvent action) {
610610
case CMD_IMPROVE: {
611611
String type = data[1];
612612
int cost = MathUtility.parseInt(data[2]);
613-
int oldExpLevel = selectedPerson.getExperienceLevel(getCampaign(), false);
614613
selectedPerson.improveSkill(type);
615614
selectedPerson.spendXP(cost);
616615

617616
PerformanceLogger.improvedSkill(getCampaign(),
618617
selectedPerson,
619618
getCampaign().getLocalDate(),
620619
selectedPerson.getSkill(type).getType().getName(),
621-
selectedPerson.getSkill(type)
622-
.toString(selectedPerson.getOptions(), selectedPerson.getReputation()));
620+
selectedPerson.getSkill(type).toString());
623621
getCampaign().addReport(String.format(resources.getString("improved.format"),
624622
selectedPerson.getHyperlinkedName(),
625623
type));
@@ -2874,7 +2872,10 @@ protected Optional<JPopupMenu> createPopupMenu() {
28742872
traitsMenu.add(menuItem);
28752873

28762874
// Reputation
2877-
int reputation = person.getReputation();
2875+
int reputation = person.getAdjustedReputation(getCampaignOptions().isUseAgeEffects(),
2876+
getCampaign().isClanCampaign(),
2877+
getCampaign().getLocalDate(),
2878+
person.getRankLevel());
28782879
target = reputation + 1;
28792880
menuItem = new JMenuItem(String.format(resources.getString("spendOnReputation.text"), target, traitCost));
28802881
menuItem.setToolTipText(wordWrap(String.format(resources.getString("spendOnReputation.tooltip"),

MekHQ/src/mekhq/gui/dialog/BatchXPDialog.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ private void spendXP() {
377377
person,
378378
campaign.getLocalDate(),
379379
person.getSkill(skillName).getType().getName(),
380-
person.getSkill(skillName).toString(person.getOptions(), person.getReputation()));
380+
person.getSkill(skillName).toString());
381381
campaign.personUpdated(person);
382382
}
383383

0 commit comments

Comments
 (0)