diff --git a/MekHQ/docs/Awards Module.pdf b/MekHQ/docs/Personnel Modules/Awards Module.pdf
similarity index 100%
rename from MekHQ/docs/Awards Module.pdf
rename to MekHQ/docs/Personnel Modules/Awards Module.pdf
diff --git a/MekHQ/docs/Education Module.pdf b/MekHQ/docs/Personnel Modules/Education Module.pdf
similarity index 100%
rename from MekHQ/docs/Education Module.pdf
rename to MekHQ/docs/Personnel Modules/Education Module.pdf
diff --git a/MekHQ/docs/Personnel Modules/Turnover & Retention Module.pdf b/MekHQ/docs/Personnel Modules/Turnover & Retention Module.pdf
new file mode 100644
index 0000000000..3ae2214400
Binary files /dev/null and b/MekHQ/docs/Personnel Modules/Turnover & Retention Module.pdf differ
diff --git a/MekHQ/resources/mekhq/resources/AtBScenarioBuiltIn.properties b/MekHQ/resources/mekhq/resources/AtBScenarioBuiltIn.properties
index 1d881d48ca..91a14fc420 100644
--- a/MekHQ/resources/mekhq/resources/AtBScenarioBuiltIn.properties
+++ b/MekHQ/resources/mekhq/resources/AtBScenarioBuiltIn.properties
@@ -1,5 +1,5 @@
battleDetails.baseAttack.attacker.victory=Destroy all base forces and 50% of the other enemy units.\nKeep more than 50% of your+ally's units operational.
-battleDetails.baseAttack.attacker.observations=Winner controls the battlefield after the battle. If player wins the battle and the contract required lance type is fight or scout, it ends with an early victory. Otherwise the enemy morale level becomes rout.
+battleDetails.baseAttack.attacker.observations=Winner controls the battlefield after the battle. If player wins the battle and the contract required lance type is fight or scout, it ends with an early victory. Otherwise the enemy morale level becomes broken.
battleDetails.baseAttack.defender.victory=Must keep at least three base units operational and destroy 50% of enemy forces.\nKeep more than 50% of your+ally's units operational.
battleDetails.baseAttack.defender.observations=If player loses, the contract ends early with a contract defeat.
@@ -91,7 +91,7 @@ battleDetails.reconRaid.instructions.returnEdge=Return to %s map edge.
battleDetails.reconRaid.instructions.reward=1d6-2 bonus rolls if victorious.
battleDetails.baseAttack.attacker.details.winnerFightScout=Completing this objective will end the contract with an early victory.
-battleDetails.baseAttack.attacker.details.winnerDefendTraining=Completing this objective will set the enemy morale to "Rout".
+battleDetails.baseAttack.attacker.details.winnerDefendTraining=Completing this objective will set the enemy morale to "Broken".
battleDetails.baseAttack.attacker.details.loser=Losing this battle will end the contract with an early defeat.
battleDetails.starLeagueCache.Mek=Star League Mek
diff --git a/MekHQ/resources/mekhq/resources/Campaign.properties b/MekHQ/resources/mekhq/resources/Campaign.properties
index 9176293f15..107ab5a2e4 100644
--- a/MekHQ/resources/mekhq/resources/Campaign.properties
+++ b/MekHQ/resources/mekhq/resources/Campaign.properties
@@ -38,7 +38,31 @@ LayeredForceIconLayer.FRAME.toolTipText=This tab contains frames, which are the
LayeredForceIconLayer.LOGO.text=Logos
LayeredForceIconLayer.LOGO.toolTipText=This tab contains canon faction logos that can be added to the center of a force icon.
+#### Turnover and Retention
+turnoverBurnedOut.text=burnt out.
+turnoverPoached.text=was poached by another force.
+turnoverJointDeparture.text=departed with their spouse.
+turnoverJointDepartureChild.text=departed with their parent.
+turnoverEmployeeTurnoverDialog.text=Employee Turnover
+turnoverPayoutDialog.text=Show Payout Dialog
+turnoverNotNow.text=Not Now
+turnoverCancel.text=Cancel
+
+turnoverRollRequired.text=Employee Turnover Check Required
+turnoverDialogDescription.text=It has been a %s since the last Employee Turnover roll.
+
+turnoverWeekly.text=7 days
+turnoverMonthly.text=28 days
+turnoverAnnually.text=356 days
+
+turnoverFinalPayments.text=Unresolved Final Payments
+turnoverPersonnelKilled.text=
= 50) {
+ getPerson(pid).changeStatus(this, getLocalDate(), PersonnelStatus.RETIRED);
} else {
- getPersonnelMarket().addPerson(p);
+ getPerson(pid).changeStatus(this, getLocalDate(), PersonnelStatus.RESIGNED);
}
- }
- if (getCampaignOptions().getRandomDependentMethod().isAgainstTheBot()
- && getCampaignOptions().isUseRandomDependentAddition()) {
- int dependents = getRetirementDefectionTracker().getPayout(pid).getDependents();
- while (dependents > 0) {
- Person person = newDependent(false);
- if (recruitPerson(person)) {
- dependents--;
- } else {
- dependents = 0;
+ // if marriage modifier is enabled, couples leave together
+ if (campaignOptions.isUseMarriageModifiers()) {
+ Person spouse = person.getGenealogy().getSpouse();
+
+ if ((spouse != null) && (!getRetirementDefectionTracker().getRetirees().contains(spouse.getId()))) {
+ if ((!spouse.getStatus().isDepartedUnit()) && (!spouse.getStatus().isAbsent())) {
+ addReport(spouse.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDeparture.text"));
+ if (spouse.getPrimaryRole().isCivilian()) {
+ spouse.changeStatus(this, getLocalDate(), PersonnelStatus.RESIGNED);
+ } else {
+ spouse.changeStatus(this, getLocalDate(), PersonnelStatus.LEFT);
+ }
+ }
+ }
+ }
+
+ // This ensures children have a chance of following their parent into departure
+ for (Person child : person.getGenealogy().getChildren()) {
+ if ((child.isChild(getLocalDate())) && (!child.getStatus().isDepartedUnit())) {
+ if (campaignOptions.isUseMarriageModifiers()) {
+ addReport(child.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDepartureChild.text"));
+ child.changeStatus(this, getLocalDate(), PersonnelStatus.LEFT);
+ } else {
+ boolean remainingParent = child.getGenealogy().getParents().stream()
+ .anyMatch(parent -> (!parent.getStatus().isDepartedUnit()) && (!parent.getStatus().isAbsent()));
+
+ if ((!remainingParent) || (Compute.randomInt(2) == 0)) {
+ addReport(child.getHyperlinkedFullTitle() + ' ' + resources.getString("turnoverJointDepartureChild.text"));
+ child.changeStatus(this, getLocalDate(), PersonnelStatus.LEFT);
+ }
+ }
}
}
}
@@ -734,10 +759,10 @@ && getCampaignOptions().isUseRandomDependentAddition()) {
}
getRetirementDefectionTracker().resolveAllContracts();
return true;
- } else {
- addReport("You cannot afford to make the final payments.");
- return false;
}
+ } else {
+ addReport("You cannot afford to make the final payments.");
+ return false;
}
return true;
@@ -1230,6 +1255,12 @@ public Collection getUnits() {
return getHangar().getUnits();
}
+ public Collection getLargeCraftAndWarShips() {
+ return getHangar().getUnits().stream()
+ .filter(unit -> (unit.getEntity().isLargeCraft()) || (unit.getEntity().isWarShip()))
+ .collect(Collectors.toList());
+ }
+
public List getEntities() {
return getUnits().stream()
.map(Unit::getEntity)
@@ -1350,6 +1381,14 @@ public Person newPerson(final PersonnelRole primaryRole, final PersonnelRole sec
return person;
}
+
+ public Boolean getFieldKitchenWithinCapacity() {
+ return fieldKitchenWithinCapacity;
+ }
+
+ public void setFieldKitchenWithinCapacity(final Boolean fieldKitchenWithinCapacity) {
+ this.fieldKitchenWithinCapacity = fieldKitchenWithinCapacity;
+ }
//endregion Person Creation
//region Personnel Recruitment
@@ -1552,7 +1591,7 @@ public void checkBloodnameAdd(Person person, boolean ignoreDice) {
if (getCampaignOptions().getUnitRatingMethod().isEnabled()) {
IUnitRating rating = getUnitRating();
bloodnameTarget += IUnitRating.DRAGOON_C - (getCampaignOptions().getUnitRatingMethod().equals(
- mekhq.campaign.rating.UnitRatingMethod.FLD_MAN_MERCS_REV)
+ UnitRatingMethod.FLD_MAN_MERCS_REV)
? rating.getUnitRatingAsInteger() : rating.getModifier());
}
@@ -1620,6 +1659,16 @@ public List getActivePersonnel() {
.collect(Collectors.toList());
}
+ /**
+ * Provides a filtered list of personnel including only Persons with the AWOL status.
+ * @return a {@link Person} List
containing all active personnel
+ */
+ public List getAwolPersonnel() {
+ return getPersonnel().stream()
+ .filter(p -> p.getStatus().isAwol())
+ .collect(Collectors.toList());
+ }
+
/**
* Provides a filtered list of personnel including only Persons with the Student status.
* @return a {@link Person} List
containing all active personnel
@@ -3162,20 +3211,6 @@ && getLocation().getJumpPath().getLastSystem().getId().equals(contract.getSystem
}
}
- private void processNewDayATBFatigue() {
- boolean inContract = false;
- for (final AtBContract contract : getActiveAtBContracts()) {
- fatigueLevel += contract.getContractType().getFatigue();
- inContract = true;
- }
-
- if (!inContract && location.isOnPlanet()) {
- fatigueLevel -= 2;
- }
- fatigueLevel = Math.max(fatigueLevel, 0);
- addReport("Your fatigue level is: " + fatigueLevel);
- }
-
private void processNewDayATB() {
contractMarket.generateContractOffers(this); // TODO : AbstractContractMarket : Remove
@@ -3239,21 +3274,16 @@ && getCampaignOptions().getRandomDependentMethod().isAgainstTheBot()
if (getLocalDate().getDayOfMonth() == 1) {
/*
- * First of the month; roll morale, track unit fatigue.
+ * First of the month; roll Morale.
*/
IUnitRating rating = getUnitRating();
rating.reInitialize();
for (AtBContract contract : getActiveAtBContracts()) {
contract.checkMorale(getLocalDate(), getUnitRatingMod());
- addReport("Enemy morale is now " + contract.getMoraleLevel()
+ addReport("Enemy Morale is now " + contract.getMoraleLevel()
+ " on contract " + contract.getName());
}
-
- // Account for fatigue
- if (getCampaignOptions().isTrackUnitFatigue()) {
- processNewDayATBFatigue();
- }
}
processNewDayATBScenarios();
@@ -3261,7 +3291,7 @@ && getCampaignOptions().getRandomDependentMethod().isAgainstTheBot()
public void processNewDayPersonnel() {
// This MUST use getActivePersonnel as we only want to process active personnel, and
- // furthermore this allows us to add and remove personnel without issue
+ // furthermore, this allows us to add and remove personnel without issue
for (Person p : getActivePersonnel()) {
// Death
if (getDeath().processNewDay(this, getLocalDate(), p)) {
@@ -3522,6 +3552,8 @@ public boolean newDay() {
processNewDayPersonnel();
+ processFatigueNewDay();
+
if (campaignOptions.isUseEducationModule()) {
processEducationNewDay();
}
@@ -3575,13 +3607,29 @@ private void processEducationNewDay() {
}
}
- public @Nullable Person getFlaggedCommander() {
- for (Person p : getPersonnel()) {
- if (p.isCommander()) {
- return p;
- }
+ /**
+ * This method is responsible for handling fatigue recovery and checking if the field kitchen is within capacity.
+ * Even if fatigue is disabled, fatigue recovery is still processed to ensure that fatigued personnel are not
+ * frozen in that state.
+ */
+ private void processFatigueNewDay() {
+ // even if Fatigue is disabled, we still want to process recovery so fatigued personnel aren't frozen in that state
+ Fatigue.processFatigueRecovery(this);
+
+ if (campaignOptions.isUseFatigue()) {
+ // we store these values, so this only needs to be checked once per day,
+ // otherwise we would need to check it once for each active person in the campaign
+ fieldKitchenWithinCapacity = getActivePersonnel().size() <= Fatigue.checkFieldKitchenCapacity(this);
+ } else {
+ fieldKitchenWithinCapacity = false;
}
- return null;
+ }
+
+ public @Nullable Person getFlaggedCommander() {
+ return getPersonnel().stream()
+ .filter(Person::isCommander)
+ .findFirst()
+ .orElse(null);
}
/**
@@ -4222,7 +4270,6 @@ public void writeToXML(final PrintWriter pw) {
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lastMissionId", lastMissionId);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lastScenarioId", lastScenarioId);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "calendar", getLocalDate());
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "fatigueLevel", fatigueLevel);
MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "nameGen");
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "faction", RandomNameGenerator.getInstance().getChosenFaction());
@@ -4484,6 +4531,33 @@ public void setRankSystem(final @Nullable RankSystem rankSystem) {
public void setRankSystemDirect(final RankSystem rankSystem) {
this.rankSystem = rankSystem;
}
+
+ /**
+ * Returns the highest ranked person from the given list of personnel.
+ *
+ * @param personnel the list of personnel from which to find the highest ranked person
+ * @param useSkillTiebreaker determines whether to use a experience level tiebreaker when comparing ranks
+ * (true - use skill tiebreaker, false - do not use skill tiebreaker)
+ * @return the highest ranked person from the list, or null if the provided personnel list is empty
+ */
+ public Person getHighestRankedPerson(List personnel, boolean useSkillTiebreaker) {
+ Person highestRankedPerson = null;
+
+ if (!personnel.isEmpty()) {
+ for (Person person : personnel) {
+ if (useSkillTiebreaker) {
+ if (person.outRanksUsingSkillTiebreaker(this, highestRankedPerson)) {
+ highestRankedPerson = person;
+ }
+ } else {
+ if (person.outRanks(highestRankedPerson)) {
+ highestRankedPerson = person;
+ }
+ }
+ }
+ }
+ return highestRankedPerson;
+ }
//endregion Ranks
public void setFinances(Finances f) {
@@ -5193,34 +5267,34 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition,
* a minimum for non-flamer energy weapons, which was the reason this rule was
* included in AtB to begin with.
*/
- if (et instanceof megamek.common.weapons.lasers.EnergyWeapon
- && !(et instanceof megamek.common.weapons.flamers.FlamerWeapon)
+ if (et instanceof EnergyWeapon
+ && !(et instanceof FlamerWeapon)
&& partAvailability < EquipmentType.RATING_C) {
partAvailability = EquipmentType.RATING_C;
partAvailabilityLog.append(";(non-flamer lasers)");
}
- if (et instanceof megamek.common.weapons.autocannons.ACWeapon) {
+ if (et instanceof ACWeapon) {
partAvailability -= 2;
partAvailabilityLog.append(";(autocannon): -2");
}
- if (et instanceof megamek.common.weapons.gaussrifles.GaussWeapon
- || et instanceof megamek.common.weapons.flamers.FlamerWeapon) {
+ if (et instanceof GaussWeapon
+ || et instanceof FlamerWeapon) {
partAvailability--;
partAvailabilityLog.append(";(gauss rifle or flamer): -1");
}
- if (et instanceof megamek.common.AmmoType) {
- switch (((megamek.common.AmmoType) et).getAmmoType()) {
- case megamek.common.AmmoType.T_AC:
+ if (et instanceof AmmoType) {
+ switch (((AmmoType) et).getAmmoType()) {
+ case AmmoType.T_AC:
partAvailability -= 2;
partAvailabilityLog.append(";(autocannon ammo): -2");
break;
- case megamek.common.AmmoType.T_GAUSS:
+ case AmmoType.T_GAUSS:
partAvailability -= 1;
partAvailabilityLog.append(";(gauss ammo): -1");
break;
}
if (EnumSet.of(AmmoType.Munitions.M_STANDARD).containsAll(
- ((megamek.common.AmmoType) et).getMunitionType())){
+ ((AmmoType) et).getMunitionType())){
partAvailability--;
partAvailabilityLog.append(";(standard ammo): -1");
}
@@ -5229,10 +5303,10 @@ public TargetRoll getTargetForAcquisition(final IAcquisitionWork acquisition,
if (((getGameYear() < 2950) || (getGameYear() > 3040))
&& (acquisition instanceof Armor || acquisition instanceof MissingMekActuator
- || acquisition instanceof mekhq.campaign.parts.MissingMekCockpit
- || acquisition instanceof mekhq.campaign.parts.MissingMekLifeSupport
- || acquisition instanceof mekhq.campaign.parts.MissingMekLocation
- || acquisition instanceof mekhq.campaign.parts.MissingMekSensor)) {
+ || acquisition instanceof MissingMekCockpit
+ || acquisition instanceof MissingMekLifeSupport
+ || acquisition instanceof MissingMekLocation
+ || acquisition instanceof MissingMekSensor)) {
partAvailability--;
partAvailabilityLog.append("(Mek part prior to 2950 or after 3040): - 1");
}
@@ -6338,7 +6412,7 @@ public int calculatePartTransitTime(int mos) {
* @param part A part to lookup its current inventory.
* @return A PartInventory object detailing the current counts of
* the part on hand, in transit, and ordered.
- * @see mekhq.campaign.parts.PartInventory
+ * @see PartInventory
*/
public PartInventory getPartInventory(Part part) {
PartInventory inventory = new PartInventory();
@@ -6763,6 +6837,10 @@ public void initRetirementDateTracking() {
}
}
+ public void initTurnover() {
+ getRetirementDefectionTracker().setLastRetirementRoll(getLocalDate());
+ }
+
public void initAtB(boolean newCampaign) {
getRetirementDefectionTracker().setLastRetirementRoll(getLocalDate());
@@ -6796,13 +6874,11 @@ public void initAtB(boolean newCampaign) {
* that 'Mech (which is a less certain assumption)
*/
for (Person p : getPersonnel()) {
- LocalDate join = null;
- for (LogEntry e : p.getPersonnelLog()) {
- if (e.getDesc().startsWith("Joined ")) {
- join = e.getDate();
- break;
- }
- }
+ LocalDate join = p.getPersonnelLog().stream()
+ .filter(e -> e.getDesc().startsWith("Joined "))
+ .findFirst()
+ .map(LogEntry::getDate)
+ .orElse(null);
if ((join != null) && join.equals(founding)) {
p.setFounder(true);
}
@@ -6814,7 +6890,7 @@ public void initAtB(boolean newCampaign) {
String mech = e.getDesc().substring(12);
MechSummary ms = MechSummaryCache.getInstance().getMech(mech);
if (null != ms && (p.isFounder()
- || ms.getWeightClass() < megamek.common.EntityWeightClass.WEIGHT_ASSAULT)) {
+ || ms.getWeightClass() < EntityWeightClass.WEIGHT_ASSAULT)) {
p.setOriginalUnitWeight(ms.getWeightClass());
if (ms.isClan()) {
p.setOriginalUnitTech(Person.TECH_CLAN);
@@ -6870,42 +6946,71 @@ public boolean checkOverDueLoans() {
public boolean checkRetirementDefections() {
if (!getRetirementDefectionTracker().getRetirees().isEmpty()) {
- // FIXME : Localize
- Object[] options = { "Show Payout Dialog", "Cancel" };
- return JOptionPane.YES_OPTION == JOptionPane.showOptionDialog(null,
- "You have personnel who have left the unit or been killed in action but have not received their final payout.\nYou must deal with these payments before advancing the day.\nHere are some options:\n - Sell off equipment to generate funds.\n - Pay one or more personnel in equipment.\n - Just cheat and use GM mode to edit the settlement.",
- "Unresolved Final Payments", JOptionPane.OK_CANCEL_OPTION,
- JOptionPane.WARNING_MESSAGE, null, options, options[0]);
+ Object[] options = {
+ resources.getString("turnoverPayoutDialog.text"),
+ resources.getString("turnoverCancel.text")
+ };
+
+ return JOptionPane.YES_OPTION == JOptionPane.showOptionDialog(
+ null,
+ resources.getString("turnoverPersonnelKilled.text"),
+ resources.getString("turnoverFinalPayments.text"),
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.WARNING_MESSAGE,
+ null,
+ options,
+ options[0]
+ );
}
return false;
}
- public boolean checkYearlyRetirements() {
- if (!getCampaignOptions().getRandomRetirementMethod().isNone()
- && getCampaignOptions().isUseYearEndRandomRetirement()
- && (ChronoUnit.DAYS.between(getRetirementDefectionTracker().getLastRetirementRoll(), getLocalDate())
- == getRetirementDefectionTracker().getLastRetirementRoll().lengthOfYear())) {
- // FIXME : Localize
- Object[] options = { "Show Retirement Dialog", "Not Now" };
- return JOptionPane.YES_OPTION == JOptionPane.showOptionDialog(null,
- "It has been a year since the last Employee Turnover roll, and it is time to do another.",
- "Employee Turnover roll required", JOptionPane.OK_CANCEL_OPTION,
- JOptionPane.WARNING_MESSAGE, null, options, options[0]);
+ public boolean checkTurnoverPrompt() {
+ int days = 0;
+ String period = "";
+
+ switch (campaignOptions.getTurnoverFrequency()) {
+ case NEVER:
+ return false;
+ case WEEKLY:
+ days = 7;
+ period = resources.getString("turnoverWeekly.text");
+ break;
+ case MONTHLY:
+ days = 28;
+ period = resources.getString("turnoverMonthly.text");
+ break;
+ case ANNUALLY:
+ days = 365;
+ period = resources.getString("turnoverAnnually.text");
+ break;
+ }
+
+ if (ChronoUnit.DAYS.between(getRetirementDefectionTracker().getLastRetirementRoll(), getLocalDate()) >= days) {
+ Object[] options = {
+ resources.getString("turnoverEmployeeTurnoverDialog.text"),
+ resources.getString("turnoverNotNow.text")
+ };
+
+ return JOptionPane.YES_OPTION == JOptionPane.showOptionDialog(
+ null,
+ String.format(resources.getString("turnoverDialogDescription.text"),
+ period),
+ resources.getString("turnoverRollRequired.text"),
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.WARNING_MESSAGE,
+ null,
+ options,
+ options[0]
+ );
}
return false;
}
public boolean checkScenariosDue() {
- for(Mission m : getActiveMissions(true)) {
- for(Scenario s : m.getCurrentScenarios()) {
- if((s.getDate() != null)
- && !(s instanceof AtBScenario)
- && !getLocalDate().isBefore(s.getDate())) {
- return true;
- }
- }
- }
- return false;
+ return getActiveMissions(true).stream()
+ .flatMap(m -> m.getCurrentScenarios().stream())
+ .anyMatch(s -> (s.getDate() != null) && !(s instanceof AtBScenario) && !getLocalDate().isBefore(s.getDate()));
}
/**
diff --git a/MekHQ/src/mekhq/campaign/CampaignOptions.java b/MekHQ/src/mekhq/campaign/CampaignOptions.java
index ebdbef99d2..0d9512fb34 100644
--- a/MekHQ/src/mekhq/campaign/CampaignOptions.java
+++ b/MekHQ/src/mekhq/campaign/CampaignOptions.java
@@ -277,12 +277,52 @@ public static String getTransitUnitName(final int unit) {
// Retirement
private boolean useRetirementDateTracking;
- private RandomRetirementMethod randomRetirementMethod;
- private boolean useYearEndRandomRetirement;
+ private boolean useRandomRetirement;
+
+ private TurnoverTargetNumberMethod turnoverTargetNumberMethod;
+ private SkillLevel turnoverDifficulty;
+ private int turnoverFixedTargetNumber;
+ private boolean aeroRecruitsHaveUnits;
+ private boolean trackOriginalUnit;
+ private TurnoverFrequency turnoverFrequency;
private boolean useContractCompletionRandomRetirement;
+ private boolean useRandomFounderTurnover;
+ private boolean useFounderRetirement;
+ private boolean useSubContractSoldiers;
+ private int serviceContractDuration;
+ private int serviceContractModifier;
+ private boolean payBonusDefault;
+
private boolean useCustomRetirementModifiers;
- private boolean useRandomFounderRetirement;
- private boolean trackUnitFatigue;
+ private boolean useFatigueModifiers;
+ private boolean useSkillModifiers;
+ private boolean useAgeModifiers;
+ private boolean useUnitRatingModifiers;
+ private boolean useFactionModifiers;
+ private boolean useMissionStatusModifiers;
+ private boolean useMarriageModifiers;
+ private boolean useLoyaltyModifiers;
+ private boolean useHideLoyalty;
+
+ private int payoutRateOfficer;
+ private int payoutRateEnlisted;
+ private int payoutRetirementMultiplier;
+ private boolean usePayoutServiceBonus;
+ private int payoutServiceBonusRate;
+
+ private boolean useAdministrativeStrain;
+ private int administrativeCapacity;
+ private int multiCrewStrainDivider;
+
+ private boolean useManagementSkill;
+ private boolean useCommanderLeadershipOnly;
+ private int managementSkillPenalty;
+
+ private boolean useFatigue;
+ private int fatigueRate;
+ private boolean useInjuryFatigue;
+ private int fieldKitchenCapacity;
+ private int fatigueLeaveThreshold;
// Family
private FamilialRelationshipDisplayLevel familyDisplayLevel;
@@ -409,6 +449,11 @@ public static String getTransitUnitName(final int unit) {
private double damagedPartsValueMultiplier;
private double unrepairablePartsValueMultiplier;
private double cancelledOrderRefundMultiplier;
+
+ // Shares
+ private boolean useShareSystem;
+ private boolean sharesExcludeLargeCraft;
+ private boolean sharesForAll;
//endregion Finance Tab
//region Mercenary Tab
@@ -491,12 +536,6 @@ public static String getTransitUnitName(final int unit) {
private SkillLevel skillLevel;
// Unit Administration
- private boolean useShareSystem;
- private boolean sharesExcludeLargeCraft;
- private boolean sharesForAll;
- private boolean aeroRecruitsHaveUnits;
- private boolean useLeadership;
- private boolean trackOriginalUnit;
private boolean useAero;
private boolean useVehicles;
private boolean clanVehicles;
@@ -769,15 +808,6 @@ public CampaignOptions() {
setUseDylansRandomXP(false);
setRandomOriginOptions(new RandomOriginOptions(true));
- // Retirement
- setUseRetirementDateTracking(false);
- setRandomRetirementMethod(RandomRetirementMethod.NONE);
- setUseYearEndRandomRetirement(true);
- setUseContractCompletionRandomRetirement(true);
- setUseCustomRetirementModifiers(true);
- setUseRandomFounderRetirement(true);
- setTrackUnitFatigue(false);
-
// Family
setFamilyDisplayLevel(FamilialRelationshipDisplayLevel.SPOUSE);
@@ -919,6 +949,56 @@ public CampaignOptions() {
getAgeRangeRandomDeathFemaleValues().put(TenYearAgeRange.EIGHTY_FIVE_OR_OLDER, 12870.0);
//endregion Life Paths Tab
+ //region Turnover and Retention
+ // Retirement
+ setUseRetirementDateTracking(false);
+ setUseRandomRetirement(true);
+ setTurnoverTargetNumberMethod(TurnoverTargetNumberMethod.NEGOTIATION);
+ setTurnoverDifficulty(SkillLevel.REGULAR);
+ setTurnoverFrequency(TurnoverFrequency.MONTHLY);
+ setTurnoverFixedTargetNumber(3);
+ setAeroRecruitsHaveUnits(false);
+ setUseContractCompletionRandomRetirement(true);
+ setUseRandomFounderTurnover(true);
+ setUseFounderRetirement(true);
+ setUseSubContractSoldiers(false);
+ setServiceContractDuration(36);
+ setServiceContractModifier(5);
+ setPayBonusDefault(true);
+
+ setUseCustomRetirementModifiers(true);
+ setUseFatigueModifiers(true);
+ setUseSkillModifiers(true);
+ setUseAgeModifiers(true);
+ setUseUnitRatingModifiers(true);
+ setUseFactionModifiers(true);
+ setUseMissionStatusModifiers(true);
+ setUseMarriageModifiers(true);
+
+ setUseLoyaltyModifiers(true);
+ setUseHideLoyalty(true);
+
+ setPayoutRateOfficer(3);
+ setPayoutRateEnlisted(3);
+ setPayoutRetirementMultiplier(12);
+ setUsePayoutServiceBonus(true);
+ setPayoutServiceBonusRate(10);
+
+ setUseAdministrativeStrain(true);
+ setAdministrativeCapacity(10);
+ setMultiCrewStrainDivider(5);
+
+ setUseManagementSkill(true);
+ setUseCommanderLeadershipOnly(false);
+ setManagementSkillPenalty(-2);
+
+ setUseFatigue(true);
+ setFatigueRate(1);
+ setUseInjuryFatigue(true);
+ setFieldKitchenCapacity(150);
+ setFatigueLeaveThreshold(13);
+ //endregion Turnover and Retention
+
//region Finances Tab
payForParts = false;
payForRepairs = false;
@@ -950,6 +1030,11 @@ public CampaignOptions() {
setDamagedPartsValueMultiplier(0.33);
setUnrepairablePartsValueMultiplier(0.1);
setCancelledOrderRefundMultiplier(0.5);
+
+ // Shares
+ setUseShareSystem(false);
+ setSharesExcludeLargeCraft(true);
+ setSharesForAll(true);
//endregion Finances Tab
//region Mercenary Tab
@@ -1048,12 +1133,6 @@ public CampaignOptions() {
setSkillLevel(SkillLevel.REGULAR);
// Unit Administration
- useShareSystem = false;
- sharesExcludeLargeCraft = false;
- sharesForAll = false;
- aeroRecruitsHaveUnits = false;
- useLeadership = true;
- trackOriginalUnit = false;
useAero = false;
useVehicles = true;
clanVehicles = false;
@@ -1416,6 +1495,47 @@ public boolean isDisplayKillRecord() {
public void setDisplayKillRecord(final boolean displayKillRecord) {
this.displayKillRecord = displayKillRecord;
}
+
+ public boolean isUseFatigue() {
+ return useFatigue;
+ }
+
+ public void setUseFatigue(final boolean useFatigue) {
+ this.useFatigue = useFatigue;
+ }
+
+ public Integer getFatigueRate() {
+ return fatigueRate;
+ }
+
+ public void setFatigueRate(final Integer fatigueRate) {
+ this.fatigueRate = fatigueRate;
+ }
+
+ public boolean isUseInjuryFatigue() {
+ return useInjuryFatigue;
+ }
+
+ public void setUseInjuryFatigue(final boolean useInjuryFatigue) {
+ this.useInjuryFatigue = useInjuryFatigue;
+ }
+
+ public Integer getFieldKitchenCapacity() {
+ return fieldKitchenCapacity;
+ }
+
+ public void setFieldKitchenCapacity(final Integer fieldKitchenCapacity) {
+ this.fieldKitchenCapacity = fieldKitchenCapacity;
+ }
+
+ public Integer getFatigueLeaveThreshold() {
+ return fatigueLeaveThreshold;
+ }
+
+ public void setFatigueLeaveThreshold(final Integer fatigueLeaveThreshold) {
+ this.fatigueLeaveThreshold = fatigueLeaveThreshold;
+ }
+
//endregion General Personnel
//region Expanded Personnel Information
@@ -1678,20 +1798,36 @@ public void setUseRetirementDateTracking(final boolean useRetirementDateTracking
this.useRetirementDateTracking = useRetirementDateTracking;
}
- public RandomRetirementMethod getRandomRetirementMethod() {
- return randomRetirementMethod;
+ public boolean isUseRandomRetirement() {
+ return useRandomRetirement;
+ }
+
+ public void setUseRandomRetirement(final boolean useRandomRetirement) {
+ this.useRandomRetirement = useRandomRetirement;
+ }
+
+ public TurnoverTargetNumberMethod getTurnoverTargetNumberMethod() {
+ return turnoverTargetNumberMethod;
+ }
+
+ public void setTurnoverTargetNumberMethod (final TurnoverTargetNumberMethod turnoverTargetNumberMethod) {
+ this.turnoverTargetNumberMethod = turnoverTargetNumberMethod;
+ }
+
+ public SkillLevel getTurnoverDifficulty() {
+ return turnoverDifficulty;
}
- public void setRandomRetirementMethod(final RandomRetirementMethod randomRetirementMethod) {
- this.randomRetirementMethod = randomRetirementMethod;
+ public void setTurnoverDifficulty (final SkillLevel turnoverDifficulty) {
+ this.turnoverDifficulty = turnoverDifficulty;
}
- public boolean isUseYearEndRandomRetirement() {
- return useYearEndRandomRetirement;
+ public TurnoverFrequency getTurnoverFrequency() {
+ return turnoverFrequency;
}
- public void setUseYearEndRandomRetirement(final boolean useYearEndRandomRetirement) {
- this.useYearEndRandomRetirement = useYearEndRandomRetirement;
+ public void setTurnoverFrequency (final TurnoverFrequency turnoverFrequency) {
+ this.turnoverFrequency = turnoverFrequency;
}
public boolean isUseContractCompletionRandomRetirement() {
@@ -1710,20 +1846,220 @@ public void setUseCustomRetirementModifiers(final boolean useCustomRetirementMod
this.useCustomRetirementModifiers = useCustomRetirementModifiers;
}
- public boolean isUseRandomFounderRetirement() {
- return useRandomFounderRetirement;
+ public boolean isUseFatigueModifiers() {
+ return useFatigueModifiers;
+ }
+
+ public void setUseFatigueModifiers(final boolean useFatigueModifiers) {
+ this.useFatigueModifiers = useFatigueModifiers;
+ }
+
+ public boolean isUseLoyaltyModifiers() {
+ return useLoyaltyModifiers;
+ }
+
+ public void setUseLoyaltyModifiers(final boolean useLoyaltyModifiers) {
+ this.useLoyaltyModifiers = useLoyaltyModifiers;
+ }
+
+ public boolean isUseHideLoyalty() {
+ return useHideLoyalty;
+ }
+
+ public void setUseHideLoyalty(final boolean useHideLoyalty) {
+ this.useHideLoyalty = useHideLoyalty;
+ }
+
+ public boolean isUseRandomFounderTurnover() {
+ return useRandomFounderTurnover;
+ }
+
+ public void setUseRandomFounderTurnover(final boolean useRandomFounderTurnover) {
+ this.useRandomFounderTurnover = useRandomFounderTurnover;
+ }
+
+ public boolean isUseFounderRetirement() {
+ return useFounderRetirement;
+ }
+
+ public void setUseFounderRetirement(final boolean useFounderRetirement) {
+ this.useFounderRetirement = useFounderRetirement;
+ }
+
+ public boolean isUseSubContractSoldiers() {
+ return useSubContractSoldiers;
+ }
+
+ public void setUseSubContractSoldiers(final boolean useSubContractSoldiers) {
+ this.useSubContractSoldiers = useSubContractSoldiers;
+ }
+
+ public Integer getTurnoverFixedTargetNumber() {
+ return turnoverFixedTargetNumber;
+ }
+
+ public void setTurnoverFixedTargetNumber(final Integer turnoverFixedTargetNumber) {
+ this.turnoverFixedTargetNumber = turnoverFixedTargetNumber;
+ }
+
+ public Integer getPayoutRateOfficer() {
+ return payoutRateOfficer;
+ }
+
+ public void setPayoutRateOfficer(final Integer payoutRateOfficer) {
+ this.payoutRateOfficer = payoutRateOfficer;
+ }
+
+ public Integer getPayoutRateEnlisted() {
+ return payoutRateEnlisted;
+ }
+
+ public void setPayoutRateEnlisted(final Integer payoutRateEnlisted) {
+ this.payoutRateEnlisted = payoutRateEnlisted;
+ }
+
+ public Integer getPayoutRetirementMultiplier() {
+ return payoutRetirementMultiplier;
+ }
+
+ public void setPayoutRetirementMultiplier(final Integer payoutRetirementMultiplier) {
+ this.payoutRetirementMultiplier = payoutRetirementMultiplier;
+ }
+
+ public boolean isUsePayoutServiceBonus() {
+ return usePayoutServiceBonus;
}
- public void setUseRandomFounderRetirement(final boolean useRandomFounderRetirement) {
- this.useRandomFounderRetirement = useRandomFounderRetirement;
+ public void setUsePayoutServiceBonus(final boolean usePayoutServiceBonus) {
+ this.usePayoutServiceBonus = usePayoutServiceBonus;
}
- public boolean isTrackUnitFatigue() {
- return trackUnitFatigue;
+ public Integer getPayoutServiceBonusRate() {
+ return payoutServiceBonusRate;
}
- public void setTrackUnitFatigue(final boolean trackUnitFatigue) {
- this.trackUnitFatigue = trackUnitFatigue;
+ public void setPayoutServiceBonusRate(final Integer payoutServiceBonusRate) {
+ this.payoutServiceBonusRate = payoutServiceBonusRate;
+ }
+
+ public boolean isUseSkillModifiers() {
+ return useSkillModifiers;
+ }
+
+ public void setUseSkillModifiers(final boolean useSkillModifiers) {
+ this.useSkillModifiers = useSkillModifiers;
+ }
+
+ public boolean isUseAgeModifiers() {
+ return useAgeModifiers;
+ }
+
+ public void setUseAgeModifiers(final boolean useAgeModifiers) {
+ this.useAgeModifiers = useAgeModifiers;
+ }
+
+ public boolean isUseUnitRatingModifiers() {
+ return useUnitRatingModifiers;
+ }
+
+ public void setUseUnitRatingModifiers(final boolean useUnitRatingModifiers) {
+ this.useUnitRatingModifiers = useUnitRatingModifiers;
+ }
+
+ public boolean isUseFactionModifiers() {
+ return useFactionModifiers;
+ }
+
+ public void setUseFactionModifiers(final boolean useFactionModifiers) {
+ this.useFactionModifiers = useFactionModifiers;
+ }
+
+ public boolean isUseMissionStatusModifiers() {
+ return useMissionStatusModifiers;
+ }
+
+ public void setUseMissionStatusModifiers(final boolean useMissionStatusModifiers) {
+ this.useMissionStatusModifiers = useMissionStatusModifiers;
+ }
+
+ public boolean isUseMarriageModifiers() {
+ return useMarriageModifiers;
+ }
+
+ public void setUseMarriageModifiers(final boolean useMarriageModifiers) {
+ this.useMarriageModifiers = useMarriageModifiers;
+ }
+
+ public boolean isUseAdministrativeStrain() {
+ return useAdministrativeStrain;
+ }
+
+ public void setUseAdministrativeStrain(final boolean useAdministrativeStrain) {
+ this.useAdministrativeStrain = useAdministrativeStrain;
+ }
+
+ public Integer getAdministrativeCapacity() {
+ return administrativeCapacity;
+ }
+
+ public void setAdministrativeCapacity(final Integer administrativeCapacity) {
+ this.administrativeCapacity = administrativeCapacity;
+ }
+
+ public Integer getMultiCrewStrainDivider() {
+ return multiCrewStrainDivider;
+ }
+
+ public void setMultiCrewStrainDivider(final Integer multiCrewStrainDivider) {
+ this.multiCrewStrainDivider = multiCrewStrainDivider;
+ }
+
+ public boolean isUseManagementSkill() {
+ return useManagementSkill;
+ }
+
+ public void setUseManagementSkill(final boolean useManagementSkill) {
+ this.useManagementSkill = useManagementSkill;
+ }
+
+ public boolean isUseCommanderLeadershipOnly() {
+ return useCommanderLeadershipOnly;
+ }
+
+ public void setUseCommanderLeadershipOnly(final boolean useCommanderLeadershipOnly) {
+ this.useCommanderLeadershipOnly = useCommanderLeadershipOnly;
+ }
+
+ public Integer getManagementSkillPenalty() {
+ return managementSkillPenalty;
+ }
+
+ public void setManagementSkillPenalty(final Integer managementSkillPenalty) {
+ this.managementSkillPenalty = managementSkillPenalty;
+ }
+
+ public Integer getServiceContractDuration() {
+ return serviceContractDuration;
+ }
+
+ public void setServiceContractDuration(final Integer serviceContractDuration) {
+ this.serviceContractDuration = serviceContractDuration;
+ }
+
+ public Integer getServiceContractModifier() {
+ return serviceContractModifier;
+ }
+
+ public void setServiceContractModifier(final Integer serviceContractModifier) {
+ this.serviceContractModifier = serviceContractModifier;
+ }
+
+ public boolean isPayBonusDefault() {
+ return payBonusDefault;
+ }
+
+ public void setPayBonusDefault(final boolean payBonusDefault) {
+ this.payBonusDefault = payBonusDefault;
}
//endregion Retirement
@@ -3895,14 +4231,6 @@ public void setUsePlanetaryConditions(final boolean usePlanetaryConditions) {
this.usePlanetaryConditions = usePlanetaryConditions;
}
- public boolean isUseLeadership() {
- return useLeadership;
- }
-
- public void setUseLeadership(final boolean useLeadership) {
- this.useLeadership = useLeadership;
- }
-
public boolean isUseStrategy() {
return useStrategy;
}
@@ -4136,7 +4464,6 @@ public void writeToXml(final PrintWriter pw, int indent) {
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "trackTotalXPEarnings", isTrackTotalXPEarnings());
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "showOriginFaction", isShowOriginFaction());
//endregion Expanded Personnel Information
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "showOriginFaction", isShowOriginFaction());
//region Admin
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "adminsHaveNegotiation", isAdminsHaveNegotiation());
@@ -4209,12 +4536,52 @@ public void writeToXml(final PrintWriter pw, int indent) {
//region Retirement
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRetirementDateTracking", isUseRetirementDateTracking());
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomRetirementMethod", getRandomRetirementMethod().name());
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useYearEndRandomRetirement", isUseYearEndRandomRetirement());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomRetirement", isUseRandomRetirement());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "turnoverTargetNumberMethod", getTurnoverTargetNumberMethod().name());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "turnoverDifficulty", getTurnoverDifficulty().name());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "turnoverBaseTn", getTurnoverFixedTargetNumber());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "turnoverFrequency", getTurnoverFrequency().name());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "aeroRecruitsHaveUnits", isAeroRecruitsHaveUnits());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "trackOriginalUnit", isTrackOriginalUnit());
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useContractCompletionRandomRetirement", isUseContractCompletionRandomRetirement());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomFounderTurnover", isUseRandomFounderTurnover());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useFounderRetirement", isUseFounderRetirement());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useSubContractSoldiers", isUseSubContractSoldiers());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "serviceContractDuration", getServiceContractDuration());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "serviceContractModifier", getServiceContractModifier());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "payBonusDefault", isPayBonusDefault());
+
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useCustomRetirementModifiers", isUseCustomRetirementModifiers());
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomFounderRetirement", isUseRandomFounderRetirement());
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "trackUnitFatigue", isTrackUnitFatigue());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useFatigueModifiers", isUseFatigueModifiers());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useSkillModifiers", isUseSkillModifiers());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useAgeModifiers", isUseAgeModifiers());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useUnitRatingModifiers", isUseUnitRatingModifiers());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useFactionModifiers", isUseFactionModifiers());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useMissionStatusModifiers", isUseMissionStatusModifiers());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useMarriageModifiers", isUseMarriageModifiers());
+
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useLoyaltyModifiers", isUseLoyaltyModifiers());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useHideLoyalty", isUseHideLoyalty());
+
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "payoutRateOfficer", getPayoutRateOfficer());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "payoutRateEnlisted", getPayoutRateEnlisted());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "payoutRetirementMultiplier", getPayoutRetirementMultiplier());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "usePayoutServiceBonus", isUsePayoutServiceBonus());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "payoutServiceBonusRate", getPayoutServiceBonusRate());
+
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useAdministrativeStrain", isUseAdministrativeStrain());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "administrativeStrain", getAdministrativeCapacity());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "multiCrewStrainDivider", getMultiCrewStrainDivider());
+
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useManagementSkill", isUseManagementSkill());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useCommanderLeadershipOnly", isUseCommanderLeadershipOnly());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "managementSkillPenalty", getManagementSkillPenalty());
+
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useFatigue", isUseFatigue());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "fatigueRate", getFatigueRate());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useInjuryFatigue", isUseInjuryFatigue());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "fieldKitchenCapacity", getFieldKitchenCapacity());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "fatigueLeaveThreshold", getFatigueLeaveThreshold());
//endregion Retirement
//region Family
@@ -4368,6 +4735,11 @@ public void writeToXml(final PrintWriter pw, int indent) {
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "damagedPartsValueMultiplier", getDamagedPartsValueMultiplier());
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "unrepairablePartsValueMultiplier", getUnrepairablePartsValueMultiplier());
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "cancelledOrderRefundMultiplier", getCancelledOrderRefundMultiplier());
+
+ // Shares
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useShareSystem", isUseShareSystem());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "sharesExcludeLargeCraft", isSharesExcludeLargeCraft());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "sharesForAll", isSharesForAll());
//endregion Price Multipliers
//endregion Finances Tab
@@ -4422,12 +4794,7 @@ public void writeToXml(final PrintWriter pw, int indent) {
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "opForLanceTypeVehicles", getOpForLanceTypeVehicles());
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "opForUsesVTOLs", isOpForUsesVTOLs());
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useDropShips", useDropShips);
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "aeroRecruitsHaveUnits", aeroRecruitsHaveUnits);
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useShareSystem", useShareSystem);
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "sharesExcludeLargeCraft", sharesExcludeLargeCraft);
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "sharesForAll", sharesForAll);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "mercSizeLimited", mercSizeLimited);
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "trackOriginalUnit", trackOriginalUnit);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "regionalMechVariations", regionalMechVariations);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "attachedPlayerCamouflage", attachedPlayerCamouflage);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "playerControlsAttachedUnits", playerControlsAttachedUnits);
@@ -4436,7 +4803,6 @@ public void writeToXml(final PrintWriter pw, int indent) {
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useWeatherConditions", useWeatherConditions);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useLightConditions", useLightConditions);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "usePlanetaryConditions", usePlanetaryConditions);
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useLeadership", useLeadership);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useStrategy", useStrategy);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "baseStrategyDeployment", baseStrategyDeployment);
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "additionalStrategyDeployment", additionalStrategyDeployment);
@@ -4462,7 +4828,7 @@ public void writeToXml(final PrintWriter pw, int indent) {
}
public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version version) {
- LogManager.getLogger().info("Loading Campaign Options from Version " + version + " XML...");
+ LogManager.getLogger().info("Loading Campaign Options from Version {} XML...", version);
wn.normalize();
CampaignOptions retVal = new CampaignOptions();
@@ -4885,23 +5251,6 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve
retVal.setRandomOriginOptions(randomOriginOptions);
//endregion Personnel Randomization
- //region Retirement
- } else if (wn2.getNodeName().equalsIgnoreCase("useRetirementDateTracking")) {
- retVal.setUseRetirementDateTracking(Boolean.parseBoolean(wn2.getTextContent().trim()));
- } else if (wn2.getNodeName().equalsIgnoreCase("randomRetirementMethod")) {
- retVal.setRandomRetirementMethod(RandomRetirementMethod.valueOf(wn2.getTextContent().trim()));
- } else if (wn2.getNodeName().equalsIgnoreCase("useYearEndRandomRetirement")) {
- retVal.setUseYearEndRandomRetirement(Boolean.parseBoolean(wn2.getTextContent().trim()));
- } else if (wn2.getNodeName().equalsIgnoreCase("useContractCompletionRandomRetirement")) {
- retVal.setUseContractCompletionRandomRetirement(Boolean.parseBoolean(wn2.getTextContent().trim()));
- } else if (wn2.getNodeName().equalsIgnoreCase("useCustomRetirementModifiers")) {
- retVal.setUseCustomRetirementModifiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
- } else if (wn2.getNodeName().equalsIgnoreCase("useRandomFounderRetirement")) {
- retVal.setUseRandomFounderRetirement(Boolean.parseBoolean(wn2.getTextContent().trim()));
- } else if (wn2.getNodeName().equalsIgnoreCase("trackUnitFatigue")) {
- retVal.setTrackUnitFatigue(Boolean.parseBoolean(wn2.getTextContent().trim()));
- //endregion Retirement
-
//region Family
} else if (wn2.getNodeName().equalsIgnoreCase("familyDisplayLevel")
|| wn2.getNodeName().equalsIgnoreCase("displayFamilyLevel")) { // Legacy, 0.49.12 removal
@@ -5160,6 +5509,92 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve
//endregion Life Paths Tab
//region Finances Tab
+ //region Turnover and Retention
+ } else if (wn2.getNodeName().equalsIgnoreCase("useRetirementDateTracking")) {
+ retVal.setUseRetirementDateTracking(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useRandomRetirement")) {
+ retVal.setUseRandomRetirement(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("turnoverTargetNumberMethod")) {
+ retVal.setTurnoverTargetNumberMethod(TurnoverTargetNumberMethod.valueOf(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("turnoverDifficulty")) {
+ retVal.setTurnoverDifficulty(SkillLevel.valueOf(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("turnoverBaseTn")) {
+ retVal.setTurnoverFixedTargetNumber(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("turnoverFrequency")) {
+ retVal.setTurnoverFrequency(TurnoverFrequency.valueOf(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("trackOriginalUnit")) {
+ retVal.setTrackOriginalUnit(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("aeroRecruitsHaveUnits")) {
+ retVal.setAeroRecruitsHaveUnits(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useContractCompletionRandomRetirement")) {
+ retVal.setUseContractCompletionRandomRetirement(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useRandomFounderTurnover")) {
+ retVal.setUseRandomFounderTurnover(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useFounderRetirement")) {
+ retVal.setUseFounderRetirement(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useSubContractSoldiers")) {
+ retVal.setUseSubContractSoldiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("serviceContractDuration")) {
+ retVal.setServiceContractDuration(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("serviceContractModifier")) {
+ retVal.setServiceContractModifier(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("payBonusDefault")) {
+ retVal.setPayBonusDefault(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useCustomRetirementModifiers")) {
+ retVal.setUseCustomRetirementModifiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useFatigueModifiers")) {
+ retVal.setUseFatigueModifiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useSkillModifiers")) {
+ retVal.setUseSkillModifiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useAgeModifiers")) {
+ retVal.setUseAgeModifiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useUnitRatingModifiers")) {
+ retVal.setUseUnitRatingModifiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useFactionModifiers")) {
+ retVal.setUseFactionModifiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useMissionStatusModifiers")) {
+ retVal.setUseMissionStatusModifiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useMarriageModifiers")) {
+ retVal.setUseMarriageModifiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useLoyaltyModifiers")) {
+ retVal.setUseLoyaltyModifiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useHideLoyalty")) {
+ retVal.setUseHideLoyalty(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("payoutRateOfficer")) {
+ retVal.setPayoutRateOfficer(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("payoutRateEnlisted")) {
+ retVal.setPayoutRateEnlisted(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("payoutRetirementMultiplier")) {
+ retVal.setPayoutRetirementMultiplier(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("usePayoutServiceBonus")) {
+ retVal.setUsePayoutServiceBonus(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("payoutServiceBonusRate")) {
+ retVal.setPayoutServiceBonusRate(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useAdministrativeStrain")) {
+ retVal.setUseAdministrativeStrain(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("administrativeStrain")) {
+ retVal.setAdministrativeCapacity(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("multiCrewStrainDivider")) {
+ retVal.setMultiCrewStrainDivider(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useManagementSkill")) {
+ retVal.setUseManagementSkill(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useCommanderLeadershipOnly")) {
+ retVal.setUseCommanderLeadershipOnly(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("managementSkillPenalty")) {
+ retVal.setManagementSkillPenalty(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useFatigue")) {
+ retVal.setUseFatigue(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("fatigueRate")) {
+ retVal.setFatigueRate(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("useInjuryFatigue")) {
+ retVal.setUseInjuryFatigue(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("fieldKitchenCapacity")) {
+ retVal.setFieldKitchenCapacity(Integer.parseInt(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("fatigueLeaveThreshold")) {
+ retVal.setFatigueLeaveThreshold(Integer.parseInt(wn2.getTextContent().trim()));
+ //endregion Turnover and Retention
+
+ //region Finances Tab
} else if (wn2.getNodeName().equalsIgnoreCase("payForParts")) {
retVal.payForParts = Boolean.parseBoolean(wn2.getTextContent());
} else if (wn2.getNodeName().equalsIgnoreCase("payForRepairs")) {
@@ -5225,8 +5660,16 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve
retVal.setUnrepairablePartsValueMultiplier(Double.parseDouble(wn2.getTextContent().trim()));
} else if (wn2.getNodeName().equalsIgnoreCase("cancelledOrderRefundMultiplier")) {
retVal.setCancelledOrderRefundMultiplier(Double.parseDouble(wn2.getTextContent().trim()));
- //endregion Price Multipliers
- //endregion Finances Tab
+
+ // Shares
+ } else if (wn2.getNodeName().equalsIgnoreCase("useShareSystem")) {
+ retVal.setUseShareSystem(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("sharesExcludeLargeCraft")) {
+ retVal.setSharesExcludeLargeCraft(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("sharesForAll")) {
+ retVal.setSharesForAll(Boolean.parseBoolean(wn2.getTextContent().trim()));
+ //endregion Price Multipliers
+ //endregion Finances Tab
//region Markets Tab
//region Personnel Market
@@ -5323,16 +5766,6 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve
retVal.setOpForUsesVTOLs(Boolean.parseBoolean(wn2.getTextContent().trim()));
} else if (wn2.getNodeName().equalsIgnoreCase("useDropShips")) {
retVal.useDropShips = Boolean.parseBoolean(wn2.getTextContent().trim());
- } else if (wn2.getNodeName().equalsIgnoreCase("aeroRecruitsHaveUnits")) {
- retVal.aeroRecruitsHaveUnits = Boolean.parseBoolean(wn2.getTextContent().trim());
- } else if (wn2.getNodeName().equalsIgnoreCase("useShareSystem")) {
- retVal.useShareSystem = Boolean.parseBoolean(wn2.getTextContent().trim());
- } else if (wn2.getNodeName().equalsIgnoreCase("sharesExcludeLargeCraft")) {
- retVal.sharesExcludeLargeCraft = Boolean.parseBoolean(wn2.getTextContent().trim());
- } else if (wn2.getNodeName().equalsIgnoreCase("sharesForAll")) {
- retVal.sharesForAll = Boolean.parseBoolean(wn2.getTextContent().trim());
- } else if (wn2.getNodeName().equalsIgnoreCase("trackOriginalUnit")) {
- retVal.trackOriginalUnit = Boolean.parseBoolean(wn2.getTextContent().trim());
} else if (wn2.getNodeName().equalsIgnoreCase("mercSizeLimited")) {
retVal.mercSizeLimited = Boolean.parseBoolean(wn2.getTextContent().trim());
} else if (wn2.getNodeName().equalsIgnoreCase("regionalMechVariations")) {
@@ -5361,8 +5794,6 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve
retVal.useLightConditions = Boolean.parseBoolean(wn2.getTextContent().trim());
} else if (wn2.getNodeName().equalsIgnoreCase("usePlanetaryConditions")) {
retVal.usePlanetaryConditions = Boolean.parseBoolean(wn2.getTextContent().trim());
- } else if (wn2.getNodeName().equalsIgnoreCase("useLeadership")) {
- retVal.useLeadership = Boolean.parseBoolean(wn2.getTextContent().trim());
} else if (wn2.getNodeName().equalsIgnoreCase("useStrategy")) {
retVal.useStrategy = Boolean.parseBoolean(wn2.getTextContent().trim());
} else if (wn2.getNodeName().equalsIgnoreCase("baseStrategyDeployment")) {
@@ -5417,15 +5848,6 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve
retVal.getRandomOriginOptions().setExtraRandomOrigin(Boolean.parseBoolean(wn2.getTextContent().trim()));
} else if (wn2.getNodeName().equalsIgnoreCase("originDistanceScale")) { // Legacy, 0.49.7 Removal
retVal.getRandomOriginOptions().setOriginDistanceScale(Double.parseDouble(wn2.getTextContent().trim()));
- } else if (wn2.getNodeName().equalsIgnoreCase("retirementRolls")) { // Legacy - 0.49.7 Removal
- final boolean value = Boolean.parseBoolean(wn2.getTextContent().trim());
- retVal.setRandomRetirementMethod((value && retVal.isUseAtB()) ? RandomRetirementMethod.AGAINST_THE_BOT : RandomRetirementMethod.NONE);
- retVal.setUseYearEndRandomRetirement(value);
- retVal.setUseContractCompletionRandomRetirement(value);
- } else if (wn2.getNodeName().equalsIgnoreCase("customRetirementMods")) { // Legacy - 0.49.7 Removal
- retVal.setUseCustomRetirementModifiers(Boolean.parseBoolean(wn2.getTextContent().trim()));
- } else if (wn2.getNodeName().equalsIgnoreCase("foundersNeverRetire")) { // Legacy - 0.49.7 Removal
- retVal.setUseRandomFounderRetirement(Boolean.parseBoolean(wn2.getTextContent().trim()));
} else if (wn2.getNodeName().equalsIgnoreCase("atbAddDependents")) { // Legacy - 0.49.7 Removal
final boolean value = Boolean.parseBoolean(wn2.getTextContent().trim());
retVal.setRandomDependentMethod((value && retVal.isUseAtB()) ? RandomDependentMethod.AGAINST_THE_BOT : RandomDependentMethod.NONE);
diff --git a/MekHQ/src/mekhq/campaign/CampaignSummary.java b/MekHQ/src/mekhq/campaign/CampaignSummary.java
index 23aa90397a..cdc66911bc 100644
--- a/MekHQ/src/mekhq/campaign/CampaignSummary.java
+++ b/MekHQ/src/mekhq/campaign/CampaignSummary.java
@@ -24,13 +24,21 @@
import mekhq.campaign.mission.Mission;
import mekhq.campaign.mission.enums.MissionStatus;
import mekhq.campaign.personnel.Person;
+import mekhq.campaign.personnel.SkillType;
+import mekhq.campaign.personnel.turnoverAndRetention.Fatigue;
import mekhq.campaign.unit.CargoStatistics;
import mekhq.campaign.unit.HangarStatistics;
import mekhq.campaign.unit.Unit;
+import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
+import static mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker.getAdministrativeStrain;
+import static mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker.getAdministrativeStrainModifier;
+import static mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker.getCombinedSkillValues;
+
/**
* calculates and stores summary information on a campaign for use in reporting, mostly for the command center
*/
@@ -276,4 +284,44 @@ public String getTransportCapacity() {
return percentTransported + "% bay capacity" + dropshipAppend;
}
+
+ /**
+ * Generates an administrative capacity report for the Command Center.
+ *
+ * @param campaign the campaign for which the administrative capacity report is generated
+ * @return the administrative capacity report in HTML format
+ */
+ public String getAdministrativeCapacityReport(Campaign campaign) {
+ int combinedSkillValues = getCombinedSkillValues(campaign, SkillType.S_ADMIN);
+
+ StringBuilder administrativeCapacityReport = new StringBuilder()
+ .append(getAdministrativeStrain(campaign)).append(" / ")
+ .append(campaign.getCampaignOptions().getAdministrativeCapacity() * combinedSkillValues)
+ .append(" personnel");
+
+ if (getAdministrativeStrainModifier(campaign) > 0) {
+ administrativeCapacityReport
+ .append(" (+")
+ .append(getAdministrativeStrainModifier(campaign))
+ .append(')');
+ }
+
+ return administrativeCapacityReport.toString();
+ }
+
+ /**
+ * Returns a summary of fatigue related facilities.
+ *
+ * @return A summary of fatigue related facilities.
+ */
+ public String getFatigueSummary() {
+ int personnelCount = campaign.getActivePersonnel().size();
+
+ return String.format("Kitchens (%s/%s)",
+ personnelCount, Fatigue.checkFieldKitchenCapacity(campaign));
+ }
+
+ private String createCsv(Collection> coll) {
+ return StringUtils.join(coll, ",");
+ }
}
diff --git a/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java b/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java
index 862589c8f5..d800af0a87 100644
--- a/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java
+++ b/MekHQ/src/mekhq/campaign/ResolveScenarioTracker.java
@@ -40,6 +40,7 @@
import mekhq.campaign.personnel.autoAwards.AutoAwardsController;
import mekhq.campaign.personnel.enums.PersonnelStatus;
import mekhq.campaign.personnel.enums.PrisonerStatus;
+import mekhq.campaign.personnel.turnoverAndRetention.Fatigue;
import mekhq.campaign.unit.TestUnit;
import mekhq.campaign.unit.Unit;
import mekhq.campaign.unit.actions.AdjustLargeCraftAmmoAction;
@@ -1403,6 +1404,11 @@ public void resolveScenario(ScenarioStatus resolution, String report) {
MekHQ.triggerEvent(new PersonBattleFinishedEvent(person, status));
if (status.getHits() > person.getHits()) {
+ if ((campaign.getCampaignOptions().isUseFatigue())
+ && (campaign.getCampaignOptions().isUseInjuryFatigue())) {
+ person.increaseFatigue(campaign.getCampaignOptions().getFatigueRate() * (status.getHits() - person.getHits()));
+ }
+
person.setHits(status.getHits());
}
@@ -1411,6 +1417,7 @@ public void resolveScenario(ScenarioStatus resolution, String report) {
ServiceLogger.participatedInScenarioDuringMission(person, campaign.getLocalDate(),
scenario.getName(), mission.getName());
}
+
for (Kill k : status.getKills()) {
getCampaign().addKill(k);
}
@@ -1425,6 +1432,11 @@ public void resolveScenario(ScenarioStatus resolution, String report) {
}
}
+ if (!status.isDead()) {
+ person.increaseFatigue(campaign.getCampaignOptions().getFatigueRate());
+ Fatigue.processFatigueActions(campaign, person);
+ }
+
if (getCampaign().getCampaignOptions().isUseAdvancedMedical()) {
person.diagnose(getCampaign(), status.getHits());
}
@@ -1486,6 +1498,8 @@ public void resolveScenario(ScenarioStatus resolution, String report) {
getCampaign().addReport(String.format("You have convinced %s to defect.",
person.getHyperlinkedName()));
}
+
+ person.generateLoyalty(Compute.d6(2));
} else {
continue;
}
diff --git a/MekHQ/src/mekhq/campaign/Warehouse.java b/MekHQ/src/mekhq/campaign/Warehouse.java
index 409c670169..d9ee9f5c80 100644
--- a/MekHQ/src/mekhq/campaign/Warehouse.java
+++ b/MekHQ/src/mekhq/campaign/Warehouse.java
@@ -20,18 +20,19 @@
import megamek.common.annotations.Nullable;
import mekhq.MekHQ;
-import mekhq.utilities.MHQXMLUtility;
import mekhq.campaign.event.PartChangedEvent;
import mekhq.campaign.event.PartNewEvent;
import mekhq.campaign.event.PartRemovedEvent;
import mekhq.campaign.parts.AmmoStorage;
import mekhq.campaign.parts.Armor;
import mekhq.campaign.parts.Part;
+import mekhq.utilities.MHQXMLUtility;
import java.io.PrintWriter;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@@ -247,13 +248,9 @@ private Part mergePartWithExisting(Part part) {
* @return A list of spare parts in the warehouse.
*/
public List getSpareParts() {
- List spares = new ArrayList<>();
- for (Part part : getParts()) {
- if (part.isSpare()) {
- spares.add(part);
- }
- }
- return spares;
+ return getParts().stream()
+ .filter(Part::isSpare)
+ .collect(Collectors.toList());
}
/**
diff --git a/MekHQ/src/mekhq/campaign/finances/enums/TransactionType.java b/MekHQ/src/mekhq/campaign/finances/enums/TransactionType.java
index 3e69a3e3e8..bcf3d19c70 100644
--- a/MekHQ/src/mekhq/campaign/finances/enums/TransactionType.java
+++ b/MekHQ/src/mekhq/campaign/finances/enums/TransactionType.java
@@ -43,12 +43,13 @@ public enum TransactionType {
RECRUITMENT("TransactionType.RECRUITMENT.text", "TransactionType.RECRUITMENT.toolTipText"),
RENT("TransactionType.RENT.text", "TransactionType.RENT.toolTipText"),
REPAIRS("TransactionType.REPAIRS.text", "TransactionType.REPAIRS.toolTipText"),
- RETIREMENT("TransactionType.RETIREMENT.text", "TransactionType.RETIREMENT.toolTipText"),
+ PAYOUT("TransactionType.PAYOUT.text", "TransactionType.PAYOUT.toolTipText"),
SALARIES("TransactionType.SALARIES.text", "TransactionType.SALARIES.toolTipText"),
SALVAGE("TransactionType.SALVAGE.text", "TransactionType.SALVAGE.toolTipText"),
SALVAGE_EXCHANGE("TransactionType.SALVAGE_EXCHANGE.text", "TransactionType.SALVAGE_EXCHANGE.toolTipText"),
STARTING_CAPITAL("TransactionType.STARTING_CAPITAL.text", "TransactionType.STARTING_CAPITAL.toolTipText"),
TAXES("TransactionType.TAXES.text", "TransactionType.TAXES.toolTipText"),
+ THEFT("TransactionType.THEFT.text", "TransactionType.THEFT.toolTipText"),
TRANSPORTATION("TransactionType.TRANSPORTATION.text", "TransactionType.TRANSPORTATION.toolTipText"),
UNIT_PURCHASE("TransactionType.UNIT_PURCHASE.text", "TransactionType.UNIT_PURCHASE.toolTipText"),
UNIT_SALE("TransactionType.UNIT_SALE.text", "TransactionType.UNIT_SALE.toolTipText");
@@ -147,8 +148,8 @@ public boolean isRepairs() {
return this == REPAIRS;
}
- public boolean isRetirement() {
- return this == RETIREMENT;
+ public boolean isPayout() {
+ return this == PAYOUT;
}
public boolean isSalaries() {
@@ -171,6 +172,10 @@ public boolean isTaxes() {
return this == TAXES;
}
+ public boolean isTheft() {
+ return this == THEFT;
+ }
+
public boolean isTransportation() {
return this == TRANSPORTATION;
}
@@ -235,6 +240,10 @@ public static TransactionType parseFromString(final String text) {
return RANSOM;
case 17:
return EDUCATION;
+ case 18:
+ return THEFT;
+ case 19:
+ return PAYOUT;
default:
break;
}
diff --git a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java
index e69d42db9c..09cfabb1ff 100644
--- a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java
+++ b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java
@@ -47,10 +47,14 @@
import mekhq.campaign.mod.am.InjuryTypes;
import mekhq.campaign.parts.*;
import mekhq.campaign.parts.equipment.*;
-import mekhq.campaign.personnel.*;
+import mekhq.campaign.personnel.Person;
+import mekhq.campaign.personnel.PersonnelOptions;
+import mekhq.campaign.personnel.SkillType;
+import mekhq.campaign.personnel.SpecialAbility;
import mekhq.campaign.personnel.enums.FamilialRelationshipType;
import mekhq.campaign.personnel.ranks.RankSystem;
import mekhq.campaign.personnel.ranks.RankValidator;
+import mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker;
import mekhq.campaign.storyarc.StoryArc;
import mekhq.campaign.unit.Unit;
import mekhq.campaign.unit.cleanup.EquipmentUnscrambler;
@@ -720,8 +724,6 @@ private static void processInfoNode(Campaign retVal, Node wni, Version version)
retVal.setAstechPoolOvertime(Integer.parseInt(wn.getTextContent().trim()));
} else if (xn.equalsIgnoreCase("medicPool")) {
retVal.setMedicPool(Integer.parseInt(wn.getTextContent().trim()));
- } else if (xn.equalsIgnoreCase("fatigueLevel")) {
- retVal.setFatigueLevel(Integer.parseInt(wn.getTextContent().trim()));
} else if (xn.equalsIgnoreCase("id")) {
retVal.setId(UUID.fromString(wn.getTextContent().trim()));
}
diff --git a/MekHQ/src/mekhq/campaign/market/unitMarket/AbstractUnitMarket.java b/MekHQ/src/mekhq/campaign/market/unitMarket/AbstractUnitMarket.java
index ea23edad2b..f68d62c38f 100644
--- a/MekHQ/src/mekhq/campaign/market/unitMarket/AbstractUnitMarket.java
+++ b/MekHQ/src/mekhq/campaign/market/unitMarket/AbstractUnitMarket.java
@@ -25,11 +25,11 @@
import megamek.common.MechSummary;
import megamek.common.annotations.Nullable;
import mekhq.MekHQ;
-import mekhq.utilities.MHQXMLUtility;
import mekhq.campaign.Campaign;
import mekhq.campaign.market.enums.UnitMarketMethod;
import mekhq.campaign.market.enums.UnitMarketType;
import mekhq.campaign.universe.Faction;
+import mekhq.utilities.MHQXMLUtility;
import org.apache.logging.log4j.LogManager;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@@ -97,10 +97,7 @@ public void setOffers(final List offers) {
* @param quality the quality of the unit to generate
* @param priceTarget the target number used to determine the percent
*/
- protected abstract void addOffers(final Campaign campaign, final int number,
- UnitMarketType market, final int unitType,
- @Nullable Faction faction, final int quality,
- final int priceTarget);
+ public abstract void addOffers(final Campaign campaign, final int number, UnitMarketType market, final int unitType, @Nullable Faction faction, final int quality, final int priceTarget);
/**
* @param campaign the campaign to use to generate the unit
@@ -173,7 +170,7 @@ protected abstract void addOffers(final Campaign campaign, final int number,
* @param percent the percentage of the original unit cost the unit will be offered at
* @return the name of the unit that has been added to the market
*/
- protected String addSingleUnit(final Campaign campaign, final UnitMarketType market,
+ public String addSingleUnit(final Campaign campaign, final UnitMarketType market,
final int unitType, final MechSummary mechSummary,
final int percent) {
getOffers().add(new UnitMarketOffer(market, unitType, mechSummary, percent,
diff --git a/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java b/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java
index 337679dae1..d19b9cafc9 100644
--- a/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java
+++ b/MekHQ/src/mekhq/campaign/market/unitMarket/AtBMonthlyUnitMarket.java
@@ -133,7 +133,7 @@ public void generateUnitOffers(final Campaign campaign) {
}
@Override
- protected void addOffers(final Campaign campaign, final int num, UnitMarketType market,
+ public void addOffers(final Campaign campaign, final int num, UnitMarketType market,
final int unitType, @Nullable Faction faction, final int quality,
final int priceTarget) {
if (faction == null) {
diff --git a/MekHQ/src/mekhq/campaign/market/unitMarket/DisabledUnitMarket.java b/MekHQ/src/mekhq/campaign/market/unitMarket/DisabledUnitMarket.java
index dfa3207ce7..f0e475892d 100644
--- a/MekHQ/src/mekhq/campaign/market/unitMarket/DisabledUnitMarket.java
+++ b/MekHQ/src/mekhq/campaign/market/unitMarket/DisabledUnitMarket.java
@@ -68,7 +68,7 @@ public void generateUnitOffers(final Campaign campaign) {
}
@Override
- protected void addOffers(final Campaign campaign, final int number, final UnitMarketType market,
+ public void addOffers(final Campaign campaign, final int number, final UnitMarketType market,
final int unitType, final Faction faction, final int quality,
final int priceTarget) {
diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java
index f47fe80f6c..b80d1b66fc 100644
--- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java
+++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java
@@ -288,7 +288,7 @@ public void checkMorale(LocalDate today, int dragoonRating) {
routEnd = null;
updateEnemy(today); // mix it up a little
} else {
- setMoraleLevel(AtBMoraleLevel.ROUT);
+ setMoraleLevel(AtBMoraleLevel.BROKEN);
}
return;
}
diff --git a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java
index adf8cf53c8..05b626b345 100644
--- a/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java
+++ b/MekHQ/src/mekhq/campaign/mission/atb/AtBScenarioFactory.java
@@ -109,7 +109,7 @@ public static void registerScenario(IAtBScenario scenario) {
/**
* Iterate through the list of lances and make a scenario roll for each,
* then sort them by date before adding them to the campaign.
- * Contracts with enemy morale level of invincible have a base attack
+ * Contracts with enemy morale level of unbreakable have a base attack
* (defender) scenario each week. If there is a base attack (attacker)
* scenario, that is the only one for the week on that contracts.
*
@@ -191,7 +191,7 @@ public static void createScenariosForNewWeek(Campaign c) {
assignedLances.add(lance.getForceId());
// We care if the scenario is a Base Attack, as one must be generated if the
- // current contract's morale is Invincible
+ // current contract's morale is Unbreakable
if (scenario.getScenarioType() == AtBScenario.BASEATTACK) {
hasBaseAttack = true;
@@ -208,9 +208,9 @@ public static void createScenariosForNewWeek(Campaign c) {
}
//endregion Generate Scenarios
- //region Invincible Morale Missions
- // Make sure invincible morale missions have a base attack scenario generated
- if (!hasBaseAttack && contract.getMoraleLevel().isInvincible()) {
+ //region Unbreakable Morale Missions
+ // Make sure Unbreakable morale missions have a base attack scenario generated
+ if (!hasBaseAttack && contract.getMoraleLevel().isUnbreakable()) {
/* find a lance to act as defender, giving preference
* first to those assigned to the same contract,
* then to those assigned to defense roles
@@ -272,10 +272,10 @@ public static void createScenariosForNewWeek(Campaign c) {
}
} else {
LogManager.getLogger().warn("No lances assigned to mission " + contract.getName()
- + ". Can't generate an Invincible Morale base defence mission for this force.");
+ + ". Can't generate an Unbreakable Morale base defence mission for this force.");
}
}
- //endregion Invincible Morale Missions
+ //endregion Unbreakable Morale Missions
//region Base Attack (Attacker) Generated
// If there is a base attack (attacker), it is the only one for this contract until it happens.
diff --git a/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java b/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java
index bf8ed7e692..90b6cf5b34 100644
--- a/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java
+++ b/MekHQ/src/mekhq/campaign/mission/enums/AtBContractType.java
@@ -211,28 +211,6 @@ public AtBLanceRole getRequiredLanceRole() {
}
}
- public int getFatigue() {
- switch (this) {
- case GARRISON_DUTY:
- case SECURITY_DUTY:
- case CADRE_DUTY:
- return -1;
- case RIOT_DUTY:
- case PIRATE_HUNTING:
- return 1;
- case DIVERSIONARY_RAID:
- case EXTRACTION_RAID:
- case RECON_RAID:
- case RELIEF_DUTY:
- case OBJECTIVE_RAID:
- return 2;
- case GUERRILLA_WARFARE:
- case PLANETARY_ASSAULT:
- return 3;
- default:
- return 0;
- }
- }
public int generateEventType() {
final int roll = Compute.randomInt(20) + 1;
diff --git a/MekHQ/src/mekhq/campaign/mission/enums/AtBMoraleLevel.java b/MekHQ/src/mekhq/campaign/mission/enums/AtBMoraleLevel.java
index 0dbb941719..93ad70e4f5 100644
--- a/MekHQ/src/mekhq/campaign/mission/enums/AtBMoraleLevel.java
+++ b/MekHQ/src/mekhq/campaign/mission/enums/AtBMoraleLevel.java
@@ -25,13 +25,13 @@
public enum AtBMoraleLevel {
//region Enum Declarations
- ROUT("AtBMoraleLevel.ROUT.text", "AtBMoraleLevel.ROUT.toolTipText"),
+ BROKEN("AtBMoraleLevel.BROKEN.text", "AtBMoraleLevel.BROKEN.toolTipText"),
VERY_LOW("AtBMoraleLevel.VERY_LOW.text", "AtBMoraleLevel.VERY_LOW.toolTipText"),
LOW("AtBMoraleLevel.LOW.text", "AtBMoraleLevel.LOW.toolTipText"),
NORMAL("AtBMoraleLevel.NORMAL.text", "AtBMoraleLevel.NORMAL.toolTipText"),
HIGH("AtBMoraleLevel.HIGH.text", "AtBMoraleLevel.HIGH.toolTipText"),
VERY_HIGH("AtBMoraleLevel.VERY_HIGH.text", "AtBMoraleLevel.VERY_HIGH.toolTipText"),
- INVINCIBLE("AtBMoraleLevel.INVINCIBLE.text", "AtBMoraleLevel.INVINCIBLE.toolTipText");
+ UNBREAKABLE("AtBMoraleLevel.UNBREAKABLE.text", "AtBMoraleLevel.UNBREAKABLE.toolTipText");
//endregion Enum Declarations
//region Variable Declarations
@@ -56,7 +56,7 @@ public String getToolTipText() {
//region Boolean Comparison Methods
public boolean isRout() {
- return this == ROUT;
+ return this == BROKEN;
}
public boolean isVeryLow() {
@@ -79,8 +79,8 @@ public boolean isVeryHigh() {
return this == VERY_HIGH;
}
- public boolean isInvincible() {
- return this == INVINCIBLE;
+ public boolean isUnbreakable() {
+ return this == UNBREAKABLE;
}
//endregion Boolean Comparison Methods
@@ -99,7 +99,7 @@ public static AtBMoraleLevel parseFromString(final String text) {
try {
switch (Integer.parseInt(text)) {
case 0:
- return ROUT;
+ return BROKEN;
case 1:
return VERY_LOW;
case 2:
@@ -111,7 +111,7 @@ public static AtBMoraleLevel parseFromString(final String text) {
case 5:
return VERY_HIGH;
case 6:
- return INVINCIBLE;
+ return UNBREAKABLE;
default:
break;
}
diff --git a/MekHQ/src/mekhq/campaign/personnel/Person.java b/MekHQ/src/mekhq/campaign/personnel/Person.java
index 2ee0ed6337..a397fd2495 100644
--- a/MekHQ/src/mekhq/campaign/personnel/Person.java
+++ b/MekHQ/src/mekhq/campaign/personnel/Person.java
@@ -119,11 +119,15 @@ public class Person {
private LocalDate birthday;
private LocalDate recruitment;
private LocalDate lastRankChangeDate;
- private LocalDate retirement;
private LocalDate dateOfDeath;
private List personnelLog;
private List scenarioLog;
+ private LocalDate retirement;
+ private int loyalty;
+ private int fatigue;
+ private Boolean isRecoveringFromFatigue;
+
private Skills skills;
private PersonnelOptions options;
private int toughness;
@@ -327,6 +331,9 @@ public Person(final String preNominal, final String givenName, final String surn
recruitment = null;
lastRankChangeDate = null;
retirement = null;
+ loyalty = 0;
+ fatigue = 0;
+ isRecoveringFromFatigue = false;
skills = new Skills();
options = new PersonnelOptions();
currentEdge = 0;
@@ -972,7 +979,7 @@ public void changeStatus(final Campaign campaign, final LocalDate today,
campaign.addReport(String.format(resources.getString("returnedFromMissing.report"),
getHyperlinkedFullTitle()));
ServiceLogger.returnedFromMissing(this, campaign.getLocalDate());
- } else if (getStatus().isAWOL()) {
+ } else if (getStatus().isAwol()) {
campaign.addReport(String.format(resources.getString("returnedFromAWOL.report"),
getHyperlinkedFullTitle()));
ServiceLogger.returnedFromAWOL(this, campaign.getLocalDate());
@@ -984,6 +991,9 @@ public void changeStatus(final Campaign campaign, final LocalDate today,
setRetirement(null);
break;
case RETIRED:
+ case RESIGNED:
+ case DESERTED:
+ case DEFECTED:
campaign.addReport(String.format(status.getReportText(), getHyperlinkedFullTitle()));
ServiceLogger.retired(this, today);
if (campaign.getCampaignOptions().isUseRetirementDateTracking()) {
@@ -1134,6 +1144,24 @@ public String getTimeInService(final Campaign campaign) {
.getDisplayFormattedOutput(getRecruitment(), today);
}
+ public Integer getYearsInService(final Campaign campaign) {
+ // Get time in service based on year
+ if (getRecruitment() == null) {
+ //use "" they haven't been recruited or are dependents
+ return 0;
+ }
+
+ LocalDate today = campaign.getLocalDate();
+
+ // If the person is dead, we only care about how long they spent in service to the company
+ if (getDateOfDeath() != null) {
+ //use date of death instead of the current day
+ today = getDateOfDeath();
+ }
+
+ return Math.toIntExact(ChronoUnit.YEARS.between(getRecruitment(), today));
+ }
+
public @Nullable LocalDate getLastRankChangeDate() {
return lastRankChangeDate;
}
@@ -1159,14 +1187,6 @@ public String getTimeInRank(final Campaign campaign) {
.getDisplayFormattedOutput(getLastRankChangeDate(), today);
}
- public @Nullable LocalDate getRetirement() {
- return retirement;
- }
-
- public void setRetirement(final @Nullable LocalDate retirement) {
- this.retirement = retirement;
- }
-
public void setId(final UUID id) {
this.id = id;
}
@@ -1183,6 +1203,44 @@ public Genealogy getGenealogy() {
return genealogy;
}
+ //region Turnover and Retention
+ public @Nullable LocalDate getRetirement() {
+ return retirement;
+ }
+
+ public void setRetirement(final @Nullable LocalDate retirement) {
+ this.retirement = retirement;
+ }
+
+ public int getLoyalty() {
+ return loyalty;
+ }
+
+ public void setLoyalty(final int loyalty) {
+ this.loyalty = loyalty;
+ }
+
+ public int getFatigue() {
+ return fatigue;
+ }
+
+ public void setFatigue(final int fatigue) {
+ this.fatigue = fatigue;
+ }
+
+ public void increaseFatigue(final int fatigue) {
+ this.fatigue = this.fatigue + fatigue;
+ }
+
+ public boolean getIsRecoveringFromFatigue() {
+ return isRecoveringFromFatigue;
+ }
+
+ public void setIsRecoveringFromFatigue(final boolean isRecoveringFromFatigue) {
+ this.isRecoveringFromFatigue = isRecoveringFromFatigue;
+ }
+ //region Turnover and Retention
+
//region Pregnancy
public LocalDate getDueDate() {
return dueDate;
@@ -1592,6 +1650,9 @@ public void writeToXML(final PrintWriter pw, int indent, final Campaign campaign
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "recruitment", getRecruitment());
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lastRankChangeDate", getLastRankChangeDate());
MHQXMLUtility.writeSimpleXMLTag(pw, indent, "retirement", getRetirement());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "loyalty", getLoyalty());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "fatigue", getFatigue());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "isRecoveringFromFatigue", getIsRecoveringFromFatigue());
for (Skill skill : skills.getSkills()) {
skill.writeToXML(pw, indent);
}
@@ -1901,6 +1962,12 @@ public static Person generateInstanceFromXML(Node wn, Campaign c, Version versio
retVal.lastRankChangeDate = MHQXMLUtility.parseDate(wn2.getTextContent().trim());
} else if (wn2.getNodeName().equalsIgnoreCase("retirement")) {
retVal.setRetirement(MHQXMLUtility.parseDate(wn2.getTextContent().trim()));
+ } else if (wn2.getNodeName().equalsIgnoreCase("loyalty")) {
+ retVal.loyalty = Integer.parseInt(wn2.getTextContent());
+ } else if (wn2.getNodeName().equalsIgnoreCase("fatigue")) {
+ retVal.fatigue = Integer.parseInt(wn2.getTextContent());
+ } else if (wn2.getNodeName().equalsIgnoreCase("isRecoveringFromFatigue")) {
+ retVal.isRecoveringFromFatigue = Boolean.parseBoolean(wn2.getTextContent().trim());
} else if (wn2.getNodeName().equalsIgnoreCase("advantages")) {
advantages = wn2.getTextContent();
} else if (wn2.getNodeName().equalsIgnoreCase("edge")) {
@@ -2387,6 +2454,36 @@ public boolean outRanks(final @Nullable Person other) {
return getRankNumeric() > other.getRankNumeric();
}
}
+
+ /**
+ * Checks if the current person outranks another person using a skill tiebreaker.
+ * If the other person is null, it is considered that the current person outranks them.
+ * If both persons have the same rank numeric value, the rank level is compared.
+ * If both persons have the same rank numeric value and rank level, the experience levels are compared.
+ *
+ * @param campaign the campaign used to calculate the experience levels
+ * @param otherPerson the other person to compare ranks with
+ * @return true if the current person outranks the other person, false otherwise
+ */
+ public boolean outRanksUsingSkillTiebreaker(Campaign campaign, @Nullable Person otherPerson) {
+ if (otherPerson == null) {
+ return true;
+ } else if (getRankNumeric() == otherPerson.getRankNumeric()) {
+ if (getRankLevel() > otherPerson.getRankLevel()) {
+ return true;
+ } else if (getRankLevel() < otherPerson.getRankLevel()) {
+ return false;
+ } else {
+ if (getExperienceLevel(campaign, false) == otherPerson.getExperienceLevel(campaign, false)) {
+ return getExperienceLevel(campaign, true) > otherPerson.getExperienceLevel(campaign, true);
+ } else {
+ return getExperienceLevel(campaign, false) > otherPerson.getExperienceLevel(campaign, false);
+ }
+ }
+ } else {
+ return getRankNumeric() > otherPerson.getRankNumeric();
+ }
+ }
//endregion Ranks
@Override
@@ -3509,7 +3606,16 @@ public void setOriginalUnitId(final UUID originalUnitId) {
}
public void setOriginalUnit(final Unit unit) {
+ if (unit == null) {
+ originalUnitId = null;
+ originalUnitTech = 0;
+ originalUnitWeight = 0;
+
+ return;
+ }
+
originalUnitId = unit.getId();
+
if (unit.getEntity().isClan()) {
originalUnitTech = TECH_CLAN;
} else if (unit.getEntity().getTechLevel() > TechConstants.T_INTRO_BOXSET) {
@@ -3613,4 +3719,64 @@ public void fixReferences(final Campaign campaign) {
}
}
}
+
+ /**
+ * Generates the loyalty value for a person based on the given roll value.
+ * Roll should be set to 3 for normal loyalty, 2 for low loyalty, or 4 for high loyalty.
+ *
+ * @param roll the roll value used to determine loyalty
+ */
+ public void generateLoyalty(int roll) {
+ if (roll == 3) {
+ setLoyalty(-3);
+ } else if (roll == 4) {
+ setLoyalty(-2);
+ } else if (roll < 7) {
+ setLoyalty(-1);
+ } else if (roll == 18) {
+ setLoyalty(3);
+ } else if (roll >= 17) {
+ setLoyalty(2);
+ } else if (roll > 14) {
+ setLoyalty(1);
+ } else {
+ setLoyalty(0);
+ }
+ }
+
+ /**
+ * Calculates the effective fatigue for a person.
+ *
+ * @param campaign the campaign for which to calculate the effective fatigue
+ * @return the effective fatigue value
+ */
+ public int getEffectiveFatigue(Campaign campaign) {
+ int effectiveFatigue = fatigue;
+
+ if (isClanPersonnel()) {
+ effectiveFatigue -= 2;
+ }
+
+ switch (getSkillLevel(campaign, false)) {
+ case NONE:
+ case ULTRA_GREEN:
+ case GREEN:
+ case REGULAR:
+ break;
+ case VETERAN:
+ effectiveFatigue--;
+ break;
+ case ELITE:
+ case HEROIC:
+ case LEGENDARY:
+ effectiveFatigue -= 2;
+ break;
+ }
+
+ if (campaign.getFieldKitchenWithinCapacity()) {
+ effectiveFatigue--;
+ }
+
+ return effectiveFatigue;
+ }
}
diff --git a/MekHQ/src/mekhq/campaign/personnel/RetirementDefectionTracker.java b/MekHQ/src/mekhq/campaign/personnel/RetirementDefectionTracker.java
deleted file mode 100644
index e8516e11b1..0000000000
--- a/MekHQ/src/mekhq/campaign/personnel/RetirementDefectionTracker.java
+++ /dev/null
@@ -1,761 +0,0 @@
-/*
- * RetirementDefectionTracker.java
- *
- * Copyright (c) 2014 - Carl Spain. All rights reserved.
- *
- * This file is part of MekHQ.
- *
- * MekHQ is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MekHQ is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MekHQ. If not, see .
- */
-package mekhq.campaign.personnel;
-
-import megamek.common.Compute;
-import megamek.common.TargetRoll;
-import megamek.common.annotations.Nullable;
-import megamek.common.options.IOption;
-import mekhq.utilities.MHQXMLUtility;
-import mekhq.Utilities;
-import mekhq.campaign.Campaign;
-import mekhq.campaign.finances.FinancialReport;
-import mekhq.campaign.finances.Money;
-import mekhq.campaign.mission.AtBContract;
-import mekhq.campaign.mission.Mission;
-import mekhq.campaign.personnel.enums.PersonnelRole;
-import mekhq.campaign.personnel.enums.Profession;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.logging.log4j.LogManager;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import java.io.PrintWriter;
-import java.time.LocalDate;
-import java.util.*;
-
-/**
- * @author Neoancient
- *
- * Against the Bot
- * Utility class that handles Employee Turnover rolls and final payments
- * to personnel who retire/defect/get sacked and families of those killed
- * in battle.
- */
-public class RetirementDefectionTracker {
- /* In case the dialog is closed after making the retirement rolls
- * and determining payouts but before the retirees have been paid,
- * we store those results to avoid making the rolls again.
- */
- private Set rollRequired;
- private Map> unresolvedPersonnel;
- private Map payouts;
- private LocalDate lastRetirementRoll;
-
- public RetirementDefectionTracker() {
- rollRequired = new HashSet<>();
- unresolvedPersonnel = new HashMap<>();
- payouts = new HashMap<>();
- lastRetirementRoll = LocalDate.now();
- }
-
- /**
- * @param campaign the campaign to get share values for
- * @return The value of each share in C-bills
- */
- public static Money getShareValue(Campaign campaign) {
- if (!campaign.getCampaignOptions().isUseShareSystem()) {
- return Money.zero();
- }
-
- FinancialReport r = FinancialReport.calculate(campaign);
-
- Money netWorth = r.getNetWorth();
- if (campaign.getCampaignOptions().isSharesExcludeLargeCraft()) {
- netWorth = netWorth.minus(r.getLargeCraftValue());
- }
-
- int totalShares = 0;
- for (Person p : campaign.getActivePersonnel()) {
- totalShares += p.getNumShares(campaign, campaign.getCampaignOptions().isSharesForAll());
- }
-
- if (totalShares <= 0) {
- return Money.zero();
- }
-
- return netWorth.dividedBy(totalShares);
- }
-
- /**
- * @param age the age of the employee
- * @return the age-based modifier
- */
- private static int getAgeMod(int age) {
- int ageMod = 0;
- if (age <= 20) {
- ageMod = -1;
- } else if ((age >= 50) && (age < 65)) {
- ageMod = 1;
- } else if ((age >= 65) && (age < 75)) {
- ageMod = 2;
- } else if ((age >= 75) && (age < 85)) {
- ageMod = 3;
- } else if ((age >= 85) && (age < 95)) {
- ageMod = 4;
- } else if ((age >= 95) && (age < 105)) {
- ageMod = 5;
- } else if (age >= 105) {
- ageMod = 6;
- }
-
- return ageMod;
- }
-
- /**
- * Computes the target for retirement rolls for all eligible personnel; this includes
- * all active personnel who are not dependents, prisoners, or bondsmen.
- *
- * @param contract The contract that is being resolved; if the retirement roll is not due to
- * contract resolutions (e.g. > 12 months since last roll), this can be null.
- * @param campaign The campaign to calculate target numbers for
- * @return A map with person ids as key and calculated target roll as value.
- */
- public Map calculateTargetNumbers(final @Nullable AtBContract contract,
- final Campaign campaign) {
- final Map targets = new HashMap<>();
- int combatLeadershipMod = 0;
- int supportLeadershipMod = 0;
-
- if (null != contract) {
- rollRequired.add(contract.getId());
- }
-
- if (campaign.getCampaignOptions().isUseLeadership()) {
- int combat = 0;
- int proto = 0;
- int support = 0;
- for (Person p : campaign.getActivePersonnel()) {
- if (p.getPrimaryRole().isCivilian() || !p.getPrisonerStatus().isFree()) {
- continue;
- }
-
- if (p.getPrimaryRole().isSupport()) {
- support++;
- } else if ((null == p.getUnit()) ||
- ((null != p.getUnit()) && p.getUnit().isCommander(p))) {
- /*
- * The AtB rules do not state that crews count as a
- * single person for leadership purposes, but to do otherwise
- * would tax all but the most exceptional commanders of
- * vehicle or infantry units.
- */
- if (p.getPrimaryRole().isProtoMechPilot()) {
- proto++;
- } else {
- combat++;
- }
- }
- }
- combat += proto / 5;
- int max = 12;
- if ((null != campaign.getFlaggedCommander()) &&
- (null != campaign.getFlaggedCommander().getSkill(SkillType.S_LEADER))) {
- max += 6 * campaign.getFlaggedCommander().getSkill(SkillType.S_LEADER).getLevel();
- }
-
- if (combat > 2 * max) {
- combatLeadershipMod = 2;
- } else if (combat > max) {
- combatLeadershipMod = 1;
- }
-
- if (support > 2 * max) {
- supportLeadershipMod = 2;
- } else if (support > max) {
- supportLeadershipMod = 1;
- }
- }
-
- for (Person p : campaign.getActivePersonnel()) {
- if (p.getPrimaryRole().isDependent() || !p.getPrisonerStatus().isFree() || p.isDeployed() || (p.isFounder() && !campaign.getCampaignOptions().isUseRandomFounderRetirement())) {
- continue;
- }
-
- /* Infantry units retire or defect by platoon */
- if ((null != p.getUnit()) && p.getUnit().usesSoldiers() && !p.getUnit().isCommander(p)) {
- continue;
- }
-
- TargetRoll target = new TargetRoll(3, "Base");
-
- // Skill Rating modifier
- int skillRating = p.getExperienceLevel(campaign, false);
- String skillRatingDescription;
-
- switch (skillRating) {
- case -1:
- skillRatingDescription = "Unskilled";
- break;
- case 0:
- skillRatingDescription = "Ultra-Green";
- break;
- case 1:
- skillRatingDescription = "Green";
- break;
- case 2:
- skillRatingDescription = "Regular";
- break;
- case 3:
- skillRatingDescription = "Veteran";
- break;
- case 4:
- skillRatingDescription = "Elite";
- break;
- default:
- skillRatingDescription = "Error, please see log";
- LogManager.getLogger().error("RetirementDefectionTracker: Unable to parse skillRating. Returning " + skillRating);
- }
-
- target.addModifier(skillRating, skillRatingDescription);
-
- // Unit Rating modifier
- int unitRating = 0;
-
- if (campaign.getUnitRatingMod() < 1) {
- unitRating = 2;
- } else if (campaign.getUnitRatingMod() == 1) {
- unitRating = 1;
- } else if (campaign.getUnitRatingMod() > 3) {
- unitRating = -1;
- }
-
- target.addModifier(unitRating, "Unit Rating");
-
- /* Retirement rolls are made before the contract status is set */
- if ((contract != null) && (contract.getStatus().isFailed() || contract.getStatus().isBreach())) {
- target.addModifier(1, "Failed mission");
- }
-
- // Fatigue Modifiers
- if (campaign.getCampaignOptions().isTrackUnitFatigue() && (campaign.getFatigueLevel() >= 10)) {
- target.addModifier(campaign.getFatigueLevel() / 10, "Fatigue");
- }
-
- // Faction Modifiers
- if (campaign.getFaction().isPirate()) {
- target.addModifier(1, "Pirate Company");
- } else if (p.getOriginFaction().isPirate()) {
- target.addModifier(1,"Pirate");
- }
-
- if (p.getOriginFaction().isMercenary()) {
- target.addModifier(1, "Mercenary");
- }
-
- if (p.getOriginFaction().isClan()) {
- target.addModifier(-2, "Clan");
- }
-
- // Officer Modifiers
- if (p.getRank().isOfficer()) {
- target.addModifier(-1, "Officer");
- } else {
- for (Enumeration i = p.getOptions(PersonnelOptions.LVL3_ADVANTAGES); i.hasMoreElements(); ) {
- IOption ability = i.nextElement();
- if (ability.booleanValue()) {
- if (ability.getName().equals("tactical_genius")) {
- target.addModifier(1, "Non-officer tactical genius");
- break;
- }
- }
- }
- }
-
- // Old Age modifier
- int age = p.getAge(campaign.getLocalDate());
- int ageMod = getAgeMod(age);
-
- if (ageMod > 0) {
- target.addModifier(ageMod, "Age");
- }
-
- // Shares Modifiers
- if (campaign.getCampaignOptions().isUseShareSystem()) {
- /* If this retirement roll is not being made at the end
- * of a contract (e.g. >12 months since last roll), the
- * share percentage should still apply. In the case of multiple
- * active contracts, pick the one with the best percentage.
- */
- AtBContract c = contract;
- if (c == null) {
- for (AtBContract atBContract : campaign.getActiveAtBContracts()) {
- if ((c == null) || (c.getSharesPct() < atBContract.getSharesPct())) {
- c = atBContract;
- }
- }
- }
- if (c != null) {
- target.addModifier(-((c.getSharesPct() - 20) / 10), "Shares");
- }
- }
-
- // Role Modifiers
- if (p.getPrimaryRole().isSoldier()) {
- target.addModifier(-1, p.getPrimaryRole().toString());
- }
-
- // Injury Modifiers
- int injuryMod = 0;
- for (Injury i : p.getInjuries()) {
- if (i.isPermanent()) {
- injuryMod++;
- }
- }
-
- if (injuryMod > 0) {
- target.addModifier(injuryMod, "Permanent Injuries");
- }
-
- // Leadership Modifiers
- if ((combatLeadershipMod != 0) && p.getPrimaryRole().isCombat()) {
- target.addModifier(combatLeadershipMod, "Leadership (Combatant)");
- }
-
- if ((supportLeadershipMod != 0) && p.getPrimaryRole().isSupport()) {
- target.addModifier(supportLeadershipMod, "Leadership (Support)");
- }
-
- targets.put(p.getId(), target);
- }
- return targets;
- }
-
- /**
- * Makes rolls for Employee Turnover based on previously calculated target rolls,
- * and tracks all retirees in the unresolvedPersonnel hash in case the dialog
- * is closed before payments are resolved, to avoid re-rolling the results.
- *
- * @param mission Nullable mission value
- * @param targets The hash previously generated by calculateTargetNumbers.
- * @param shareValue The value of each share in the unit; if not using the share system, this is zero.
- * @param campaign
- */
- public void rollRetirement(final @Nullable Mission mission, final Map targets,
- final Money shareValue, final Campaign campaign) {
- if ((mission != null) && !unresolvedPersonnel.containsKey(mission.getId())) {
- unresolvedPersonnel.put(mission.getId(), new HashSet<>());
- }
-
- for (UUID id : targets.keySet()) {
- if (Compute.d6(2) < targets.get(id).getValue()) {
- if (mission != null) {
- unresolvedPersonnel.get(mission.getId()).add(id);
- }
- payouts.put(id, new Payout(campaign, campaign.getPerson(id),
- shareValue, false, campaign.getCampaignOptions().isSharesForAll()));
- }
- }
-
- if (mission != null) {
- rollRequired.remove(mission.getId());
- }
-
- lastRetirementRoll = campaign.getLocalDate();
- }
-
- public LocalDate getLastRetirementRoll() {
- return lastRetirementRoll;
- }
-
- public void setLastRetirementRoll(LocalDate lastRetirementRoll) {
- this.lastRetirementRoll = lastRetirementRoll;
- }
-
- /**
- * Handles final payout to any personnel who are sacked or killed in battle
- *
- * @param person The person to be removed from the campaign
- * @param killed True if killed in battle, false if sacked
- * @param campaign
- * @param contract If not null, the payout must be resolved before the contract can be resolved.
- * @return true if the person is due a payout; otherwise false
- */
- public boolean removeFromCampaign(Person person, boolean killed, Campaign campaign,
- AtBContract contract) {
- /* Payouts to Infantry/Battle armor platoons/squads/points are
- * handled as a unit in the AtB rules, so we're just going to ignore
- * them here.
- */
- if (person.getPrimaryRole().isSoldierOrBattleArmour() || !person.getPrisonerStatus().isFree()) {
- return false;
- }
- payouts.put(person.getId(), new Payout(campaign, person, getShareValue(campaign),
- killed, campaign.getCampaignOptions().isSharesForAll()));
- if (null != contract) {
- unresolvedPersonnel.computeIfAbsent(contract.getId(), k -> new HashSet<>());
- unresolvedPersonnel.get(contract.getId()).add(person.getId());
- }
- return true;
- }
-
- public void removePayout(Person person) {
- payouts.remove(person.getId());
- }
-
- /**
- * Clears out an individual entirely from this tracker.
- * @param person The person to remove
- */
- public void removePerson(Person person) {
- payouts.remove(person.getId());
-
- for (int contractID : unresolvedPersonnel.keySet()) {
- unresolvedPersonnel.get(contractID).remove(person.getId());
- }
- }
-
- /**
- * Worker function that clears out any orphan Employee Turnover records
- */
- public void cleanupOrphans(Campaign campaign) {
- payouts.keySet().removeIf(personID -> campaign.getPerson(personID) == null);
-
- for (int contractID : unresolvedPersonnel.keySet()) {
- unresolvedPersonnel.get(contractID).removeIf(personID -> campaign.getPerson(personID) == null);
- }
- }
-
- public boolean isOutstanding(int id) {
- return unresolvedPersonnel.containsKey(id);
- }
-
- /* Called by when all payouts have been resolved for the contract.
- * If contract is null, the dialog has been invoked without a
- * specific contract and all outstanding payouts have been resolved.
- */
- public void resolveAllContracts() {
- resolveContract(null);
- payouts.clear();
- }
-
- public void resolveContract(final @Nullable Mission mission) {
- if (mission == null) {
- unresolvedPersonnel.keySet().forEach(this::resolveContract);
- unresolvedPersonnel.clear();
- } else {
- resolveContract(mission.getId());
- unresolvedPersonnel.remove(mission.getId());
- }
- }
-
- private void resolveContract(int contractId) {
- if (null != unresolvedPersonnel.get(contractId)) {
- for (UUID pid : unresolvedPersonnel.get(contractId)) {
- payouts.remove(pid);
- }
- }
- rollRequired.remove(contractId);
- }
-
- public Set getRetirees() {
- return getRetirees(null);
- }
-
- public Set getRetirees(final @Nullable Mission mission) {
- return (mission == null) ? payouts.keySet() : unresolvedPersonnel.get(mission.getId());
- }
-
- public Payout getPayout(UUID id) {
- return payouts.get(id);
- }
-
- /**
- * @param campaign the campaign the person is a part of
- * @param person the person to get the bonus cost for
- * @return The amount in C-bills required to get a bonus to the Employee Turnover roll
- */
- public static Money getBonusCost(final Campaign campaign, Person person) {
- return person.getSalary(campaign).multipliedBy(24);
- }
-
- /**
- * Class used to record the required payout to each retired/defected/killed/sacked
- * person.
- */
- public static class Payout {
- private int weightClass = 0;
- private int dependents = 0;
- private Money payoutAmount = Money.zero();
- private boolean recruit = false;
- private PersonnelRole recruitRole = PersonnelRole.NONE;
- private boolean heir = false;
- private boolean stolenUnit = false;
- private UUID stolenUnitId = null;
-
- public Payout() {
-
- }
-
- public Payout(final Campaign campaign, final Person person, final Money shareValue,
- final boolean killed, final boolean sharesForAll) {
- calculatePayout(campaign, person, killed, shareValue.isPositive());
- if (shareValue.isPositive()) {
- payoutAmount = payoutAmount.plus(shareValue.multipliedBy(person.getNumShares(campaign, sharesForAll)));
- }
- if (killed) {
- switch (Compute.d6()) {
- case 2:
- dependents = 1;
- break;
- case 3:
- dependents = Compute.d6();
- break;
- case 4:
- case 5:
- recruit = true;
- break;
- case 6:
- heir = true;
- break;
- default:
- break;
- }
- }
- }
-
- private void calculatePayout(final Campaign campaign, final Person person,
- final boolean killed, final boolean shareSystem) {
- int roll;
- if (killed) {
- roll = Utilities.dice(1, 5);
- } else {
- roll = Compute.d6() + Math.max(-1, person.getExperienceLevel(campaign, false) - 2);
- if (person.getRank().isOfficer()) {
- roll += 1;
- }
- }
-
- if (roll >= 6 && (person.getPrimaryRole().isAerospacePilot() || person.getSecondaryRole().isAerospacePilot())) {
- stolenUnit = true;
- } else {
- final Profession profession = Profession.getProfessionFromPersonnelRole(person.getPrimaryRole());
- if (profession.isInfantry()) {
- if (person.getUnit() != null) {
- payoutAmount = Money.of(50000);
- }
- } else {
- payoutAmount = getBonusCost(campaign, person);
- }
-
- if (!shareSystem && (profession.isMechWarrior() || profession.isAerospace())
- && (person.getOriginalUnitWeight() > 0)) {
- weightClass = person.getOriginalUnitWeight() + person.getOriginalUnitTech();
- if (roll <= 1) {
- weightClass--;
- } else if (roll >= 5) {
- weightClass++;
- }
- }
- }
- }
-
- public int getWeightClass() {
- return weightClass;
- }
-
- public void setWeightClass(int weight) {
- weightClass = weight;
- }
-
- public int getDependents() {
- return dependents;
- }
-
- public void setDependents(int d) {
- dependents = d;
- }
-
- public Money getPayoutAmount() {
- return payoutAmount;
- }
-
- public void setPayoutAmount(Money payoutAmount) {
- this.payoutAmount = payoutAmount;
- }
-
- public boolean hasRecruit() {
- return recruit;
- }
-
- public void setRecruit(boolean r) {
- recruit = r;
- }
-
- public PersonnelRole getRecruitRole() {
- return recruitRole;
- }
-
- public void setRecruitRole(PersonnelRole role) {
- recruitRole = role;
- }
-
- public boolean hasHeir() {
- return heir;
- }
-
- public void setHeir(boolean h) {
- heir = h;
- }
-
- public boolean hasStolenUnit() {
- return stolenUnit;
- }
-
- public void setStolenUnit(boolean stolen) {
- stolenUnit = stolen;
- }
-
- public UUID getStolenUnitId() {
- return stolenUnitId;
- }
-
- public void setStolenUnitId(UUID id) {
- stolenUnitId = id;
- }
- }
-
- private String createCsv(Collection> coll) {
- return StringUtils.join(coll, ",");
- }
-
- public void writeToXML(final PrintWriter pw, int indent) {
- MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "retirementDefectionTracker");
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "rollRequired", createCsv(rollRequired));
- MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "unresolvedPersonnel");
- for (Integer i : unresolvedPersonnel.keySet()) {
- MHQXMLUtility.writeSimpleXMLAttributedTag(pw, indent, "contract", "id", i, createCsv(unresolvedPersonnel.get(i)));
- }
- MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "unresolvedPersonnel");
-
- MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "payouts");
- for (UUID pid : payouts.keySet()) {
- MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "payout", "id", pid);
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "weightClass", payouts.get(pid).getWeightClass());
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "dependents", payouts.get(pid).getDependents());
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "cbills", payouts.get(pid).getPayoutAmount());
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "recruit", payouts.get(pid).hasRecruit());
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "heir", payouts.get(pid).hasHeir());
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "stolenUnit", payouts.get(pid).hasStolenUnit());
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "stolenUnitId", payouts.get(pid).getStolenUnitId());
- MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "payout");
- }
- MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "payouts");
-
- MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lastRetirementRoll", lastRetirementRoll);
- MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "retirementDefectionTracker");
- }
-
- public static RetirementDefectionTracker generateInstanceFromXML(Node wn, Campaign c) {
- RetirementDefectionTracker retVal = null;
-
- try {
- // Instantiate the correct child class, and call its parsing function.
- retVal = new RetirementDefectionTracker();
-
- // Okay, now load Part-specific fields!
- NodeList nl = wn.getChildNodes();
-
- // Loop through the nodes and load our contract offers
- for (int x = 0; x < nl.getLength(); x++) {
- Node wn2 = nl.item(x);
-
- // If it's not an element node, we ignore it.
- if (wn2.getNodeType() != Node.ELEMENT_NODE) {
- continue;
- }
-
- if (wn2.getNodeName().equalsIgnoreCase("rollRequired")) {
- if (!wn2.getTextContent().isBlank()) {
- String [] ids = wn2.getTextContent().split(",");
- for (String id : ids) {
- retVal.rollRequired.add(Integer.parseInt(id));
- }
- }
- } else if (wn2.getNodeName().equalsIgnoreCase("unresolvedPersonnel")) {
- NodeList nl2 = wn2.getChildNodes();
- for (int y = 0; y < nl2.getLength(); y++) {
- Node wn3 = nl2.item(y);
- if (wn3.getNodeType() != Node.ELEMENT_NODE) {
- continue;
- }
- if (wn3.getNodeName().equalsIgnoreCase("contract")) {
- int id = Integer.parseInt(wn3.getAttributes().getNamedItem("id").getTextContent());
- HashSet pids = new HashSet<>();
- String [] ids = wn3.getTextContent().split(",");
- for (String s : ids) {
- pids.add(UUID.fromString(s));
- }
- retVal.unresolvedPersonnel.put(id, pids);
- }
- }
- } else if (wn2.getNodeName().equalsIgnoreCase("payouts")) {
- NodeList nl2 = wn2.getChildNodes();
- for (int y = 0; y < nl2.getLength(); y++) {
- Node wn3 = nl2.item(y);
- if (wn3.getNodeType() != Node.ELEMENT_NODE) {
- continue;
- }
- if (wn3.getNodeName().equalsIgnoreCase("payout")) {
- UUID pid = UUID.fromString(wn3.getAttributes().getNamedItem("id").getTextContent());
- Payout payout = new Payout();
- NodeList nl3 = wn3.getChildNodes();
- for (int z = 0; z < nl3.getLength(); z++) {
- Node wn4 = nl3.item(z);
- if (wn4.getNodeType() != Node.ELEMENT_NODE) {
- continue;
- }
- if (wn4.getNodeName().equalsIgnoreCase("weightClass")) {
- payout.setWeightClass(Integer.parseInt(wn4.getTextContent()));
- } else if (wn4.getNodeName().equalsIgnoreCase("dependents")) {
- payout.setDependents(Integer.parseInt(wn4.getTextContent()));
- } else if (wn4.getNodeName().equalsIgnoreCase("c-bills")) {
- payout.setPayoutAmount(Money.fromXmlString(wn4.getTextContent().trim()));
- } else if (wn4.getNodeName().equalsIgnoreCase("recruit")) {
- payout.setRecruit(Boolean.parseBoolean(wn4.getTextContent()));
- } else if (wn4.getNodeName().equalsIgnoreCase("heir")) {
- payout.setHeir(Boolean.parseBoolean(wn4.getTextContent()));
- } else if (wn4.getNodeName().equalsIgnoreCase("stolenUnit")) {
- payout.setStolenUnit(Boolean.parseBoolean(wn4.getTextContent()));
- } else if (wn4.getNodeName().equalsIgnoreCase("stolenUnitId")) {
- payout.setStolenUnitId(UUID.fromString(wn4.getTextContent()));
- }
- }
- retVal.payouts.put(pid, payout);
- }
- }
- } else if (wn2.getNodeName().equalsIgnoreCase("lastRetirementRoll")) {
- retVal.setLastRetirementRoll(MHQXMLUtility.parseDate(wn2.getTextContent().trim()));
- }
- }
- } catch (Exception ex) {
- LogManager.getLogger().error("RetirementDefectionTracker: either the class name is invalid or the listed name doesn't exist.", ex);
- }
-
- if (retVal != null) {
- // sometimes, a campaign may be loaded with orphan records in the Employee Turnover tracker
- // let's clean those up here.
- retVal.cleanupOrphans(c);
- }
-
- return retVal;
- }
-}
diff --git a/MekHQ/src/mekhq/campaign/personnel/education/Academy.java b/MekHQ/src/mekhq/campaign/personnel/education/Academy.java
index fddc95927a..d575f2ff96 100644
--- a/MekHQ/src/mekhq/campaign/personnel/education/Academy.java
+++ b/MekHQ/src/mekhq/campaign/personnel/education/Academy.java
@@ -949,7 +949,7 @@ public String getTooltip(Campaign campaign, Person person, int courseIndex, Plan
skills = Arrays.stream(skills)
.map(String::trim)
.toArray(String[]::new);
-
+
for (String skill : skills) {
tooltip.append(skill).append(" (");
diff --git a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java
index f89d7cba18..e9c541736f 100644
--- a/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java
+++ b/MekHQ/src/mekhq/campaign/personnel/education/EducationController.java
@@ -813,6 +813,7 @@ private static void processClanWashout(Campaign campaign, Person person, Integer
case 10:
ServiceLogger.eduClanWashout(person, campaign.getLocalDate(), resources.getString("graduatedLabor.text"));
+
campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("washout.text"),
resources.getString("graduatedLabor.text"),
resources.getString("washoutLabor.text")));
@@ -911,6 +912,7 @@ private static void processWarriorCasteWashout(Campaign campaign, Person person,
int roll = Compute.randomInt(fallbackLabour);
if (roll < fallbackScientist) {
+
campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("washout.text"),
resources.getString("graduatedWarrior.text"),
resources.getString("graduatedWarriorScientist.text")));
@@ -921,6 +923,7 @@ private static void processWarriorCasteWashout(Campaign campaign, Person person,
}
if (roll < fallbackMerchant) {
+
campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("washout.text"),
resources.getString("graduatedWarrior.text"),
resources.getString("graduatedWarriorMerchant.text")));
@@ -931,6 +934,7 @@ private static void processWarriorCasteWashout(Campaign campaign, Person person,
}
if (roll < fallbackTechnician) {
+
campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("washout.text"),
resources.getString("graduatedWarrior.text"),
resources.getString("graduatedWarriorTechnician.text")));
@@ -941,6 +945,7 @@ private static void processWarriorCasteWashout(Campaign campaign, Person person,
}
// Labor
+
campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("washout.text"),
resources.getString("graduatedWarrior.text"),
resources.getString("graduatedWarriorLabor.text")));
@@ -1007,6 +1012,7 @@ private static void graduateAdult(Campaign campaign, Person person, Academy acad
// graduated with honors
if (graduationRoll >= 90) {
if (Compute.d6(1) > 5) {
+
campaign.addReport(person.getHyperlinkedName() + ' ' + String.format(resources.getString("graduatedHonors.text"),
' ' + resources.getString(graduationEventPicker())));
} else {
diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/ForceReliabilityMethod.java b/MekHQ/src/mekhq/campaign/personnel/enums/ForceReliabilityMethod.java
new file mode 100644
index 0000000000..0c21da1d0d
--- /dev/null
+++ b/MekHQ/src/mekhq/campaign/personnel/enums/ForceReliabilityMethod.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MekHQ. If not, see .
+ */
+package mekhq.campaign.personnel.enums;
+
+import mekhq.MekHQ;
+
+import java.util.ResourceBundle;
+
+public enum ForceReliabilityMethod {
+ UNIT_RATING("ForceReliabilityMethod.UNIT_RATING.text", "ForceReliabilityMethod.UNIT_RATING.toolTipText"),
+ LOYALTY("ForceReliabilityMethod.LOYALTY.text", "ForceReliabilityMethod.LOYALTY.toolTipText"),
+ OVERRIDE_A("ForceReliabilityMethod.OVERRIDE_A.text", "ForceReliabilityMethod.OVERRIDE_A.toolTipText"),
+ OVERRIDE_B("ForceReliabilityMethod.OVERRIDE_B.text", "ForceReliabilityMethod.OVERRIDE_B.toolTipText"),
+ OVERRIDE_C("ForceReliabilityMethod.OVERRIDE_C.text", "ForceReliabilityMethod.OVERRIDE_C.toolTipText"),
+ OVERRIDE_D("ForceReliabilityMethod.OVERRIDE_D.text", "ForceReliabilityMethod.OVERRIDE_D.toolTipText"),
+ OVERRIDE_F("ForceReliabilityMethod.OVERRIDE_F.text", "ForceReliabilityMethod.OVERRIDE_F.toolTipText");
+
+ private final String name;
+ private final String toolTipText;
+
+ ForceReliabilityMethod(final String name, final String toolTipText) {
+ final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel",
+ MekHQ.getMHQOptions().getLocale());
+ this.name = resources.getString(name);
+ this.toolTipText = resources.getString(toolTipText);
+ }
+
+ public String getToolTipText() {
+ return toolTipText;
+ }
+
+ public boolean isUnitRating() {
+ return this == UNIT_RATING;
+ }
+
+ public boolean isLoyalty() {
+ return this == LOYALTY;
+ }
+
+ public boolean isOverrideA() {
+ return this == OVERRIDE_A;
+ }
+
+ public boolean isOverrideB() {
+ return this == OVERRIDE_B;
+ }
+
+ public boolean isOverrideC() {
+ return this == OVERRIDE_C;
+ }
+
+ public boolean isOverrideD() {
+ return this == OVERRIDE_D;
+ }
+
+ public boolean isOverrideF() {
+ return this == OVERRIDE_F;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/RandomRetirementMethod.java b/MekHQ/src/mekhq/campaign/personnel/enums/LeadershipMethod.java
similarity index 54%
rename from MekHQ/src/mekhq/campaign/personnel/enums/RandomRetirementMethod.java
rename to MekHQ/src/mekhq/campaign/personnel/enums/LeadershipMethod.java
index 2ad0182bf7..e6d3bb537e 100644
--- a/MekHQ/src/mekhq/campaign/personnel/enums/RandomRetirementMethod.java
+++ b/MekHQ/src/mekhq/campaign/personnel/enums/LeadershipMethod.java
@@ -22,53 +22,42 @@
import java.util.ResourceBundle;
-public enum RandomRetirementMethod {
- //region Enum Declarations
- NONE("RandomRetirementMethod.NONE.text", "RandomRetirementMethod.NONE.toolTipText"),
- AGAINST_THE_BOT("RandomRetirementMethod.AGAINST_THE_BOT.text", "RandomRetirementMethod.AGAINST_THE_BOT.toolTipText");
- //endregion Enum Declarations
+public enum LeadershipMethod {
+ REGULAR("LeadershipMethod.REGULAR.text", "LeadershipMethod.REGULAR.toolTipText"),
+ GREEN("LeadershipMethod.GREEN.text", "LeadershipMethod.GREEN.toolTipText"),
+ ELITE("LeadershipMethod.ELITE.text", "LeadershipMethod.ELITE.toolTipText"),
+ FAMILY("LeadershipMethod.FAMILY.text", "LeadershipMethod.FAMILY.toolTipText"),
+ IRON_FIST("LeadershipMethod.IRON_FIST.text", "LeadershipMethod.IRON_FIST.toolTipText");
- //region Variable Declarations
private final String name;
private final String toolTipText;
- //endregion Variable Declarations
- //region Constructors
- RandomRetirementMethod(final String name, final String toolTipText) {
+ LeadershipMethod(final String name, final String toolTipText) {
final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel",
MekHQ.getMHQOptions().getLocale());
this.name = resources.getString(name);
this.toolTipText = resources.getString(toolTipText);
}
- //endregion Constructors
- //region Getters
public String getToolTipText() {
return toolTipText;
}
- //endregion Getters
- //region Boolean Comparison Methods
- public boolean isNone() {
- return this == NONE;
+ public boolean isRegular() {
+ return this == REGULAR;
}
- public boolean isAgainstTheBot() {
- return this == AGAINST_THE_BOT;
+ public boolean isElite() {
+ return this == ELITE;
}
- //endregion Boolean Comparison Methods
-/*
- public AbstractRetirement getMethod(final CampaignOptions options) {
- switch (this) {
- case AGAINST_THE_BOT:
- return new AtBRandomRetirement(options);
- case NONE:
- default:
- return new DisabledRandomRetirement(options);
- }
+ public boolean isFamily() {
+ return this == FAMILY;
+ }
+
+ public boolean isIronFist() {
+ return this == IRON_FIST;
}
-*/
@Override
public String toString() {
diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/MutinyMethod.java b/MekHQ/src/mekhq/campaign/personnel/enums/MutinyMethod.java
new file mode 100644
index 0000000000..b1283411d4
--- /dev/null
+++ b/MekHQ/src/mekhq/campaign/personnel/enums/MutinyMethod.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MekHQ. If not, see .
+ */
+package mekhq.campaign.personnel.enums;
+
+import mekhq.MekHQ;
+
+import java.util.ResourceBundle;
+
+public enum MutinyMethod {
+ CAMPAIGN_OPERATIONS("MutinyMethod.CAMPAIGN_OPERATIONS.text", "MutinyMethod.CAMPAIGN_OPERATIONS.toolTipText"),
+ ADVANCED_MUTINIES("MutinyMethod.ADVANCED_MUTINIES.text", "MutinyMethod.ADVANCED_MUTINIES.toolTipText");
+
+ private final String name;
+ private final String toolTipText;
+
+ MutinyMethod(final String name, final String toolTipText) {
+ final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel",
+ MekHQ.getMHQOptions().getLocale());
+ this.name = resources.getString(name);
+ this.toolTipText = resources.getString(toolTipText);
+ }
+
+ public String getToolTipText() {
+ return toolTipText;
+ }
+
+ public boolean isCampaignOperations() {
+ return this == CAMPAIGN_OPERATIONS;
+ }
+
+ public boolean isAdvancedMutinies() {
+ return this == ADVANCED_MUTINIES;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelStatus.java b/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelStatus.java
index a2855f6a83..ed56d7787e 100644
--- a/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelStatus.java
+++ b/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelStatus.java
@@ -37,7 +37,10 @@ public enum PersonnelStatus {
ON_LEAVE("PersonnelStatus.ON_LEAVE.text", "PersonnelStatus.ON_LEAVE.toolTipText", "PersonnelStatus.ON_LEAVE.reportText", "PersonnelStatus.ON_LEAVE.logText"),
AWOL("PersonnelStatus.AWOL.text", "PersonnelStatus.AWOL.toolTipText", "PersonnelStatus.AWOL.reportText", "PersonnelStatus.AWOL.logText"),
RETIRED("PersonnelStatus.RETIRED.text", "PersonnelStatus.RETIRED.toolTipText", "PersonnelStatus.RETIRED.reportText", "PersonnelStatus.RETIRED.logText"),
+ RESIGNED("PersonnelStatus.RESIGNED.text", "PersonnelStatus.RESIGNED.toolTipText", "PersonnelStatus.RESIGNED.reportText", "PersonnelStatus.RESIGNED.logText"),
+ LEFT("PersonnelStatus.LEFT.text", "PersonnelStatus.LEFT.toolTipText", "PersonnelStatus.LEFT.reportText", "PersonnelStatus.LEFT.logText"),
DESERTED("PersonnelStatus.DESERTED.text", "PersonnelStatus.DESERTED.toolTipText", "PersonnelStatus.DESERTED.reportText", "PersonnelStatus.DESERTED.logText"),
+ DEFECTED("PersonnelStatus.DEFECTED.text", "PersonnelStatus.DEFECTED.toolTipText", "PersonnelStatus.DEFECTED.reportText", "PersonnelStatus.DEFECTED.logText"),
STUDENT("PersonnelStatus.STUDENT.text", "PersonnelStatus.STUDENT.toolTipText", "PersonnelStatus.STUDENT.reportText", "PersonnelStatus.STUDENT.logText"),
MISSING("PersonnelStatus.MISSING.text", "PersonnelStatus.MISSING.toolTipText", "PersonnelStatus.MISSING.reportText", "PersonnelStatus.MISSING.logText"),
KIA("PersonnelStatus.KIA.text", "PersonnelStatus.KIA.toolTipText", "PersonnelStatus.KIA.reportText", "PersonnelStatus.KIA.logText"),
@@ -103,7 +106,7 @@ public boolean isOnLeave() {
return this == ON_LEAVE;
}
- public boolean isAWOL() {
+ public boolean isAwol() {
return this == AWOL;
}
@@ -111,10 +114,22 @@ public boolean isRetired() {
return this == RETIRED;
}
+ public boolean isResigned() {
+ return this == RESIGNED;
+ }
+
+ public boolean isLeft() {
+ return this == LEFT;
+ }
+
public boolean isDeserted() {
return this == DESERTED;
}
+ public boolean isDefected() {
+ return this == DEFECTED;
+ }
+
public boolean isStudent() {
return this == STUDENT;
}
@@ -171,7 +186,14 @@ public boolean isSuicide() {
* @return true if a person is currently absent from the core force, otherwise false
*/
public boolean isAbsent() {
- return isMIA() || isPoW() || isOnLeave() || isAWOL() || isStudent() || isMissing();
+ return isMIA() || isPoW() || isOnLeave() || isAwol() || isStudent() || isMissing();
+ }
+
+ /**
+ * @return true if a person has left the unit, otherwise false
+ */
+ public boolean isDepartedUnit() {
+ return isDead() || isRetired() || isResigned() || isDeserted() || isDefected() || isMissing() || isLeft();
}
/**
@@ -194,7 +216,7 @@ public boolean isDeadOrMIA() {
public static List getImplementedStatuses() {
return Stream.of(values())
.filter(personnelStatus -> !personnelStatus.isPoW() && !personnelStatus.isOnLeave()
- && !personnelStatus.isAWOL())
+ && !personnelStatus.isAwol())
.collect(Collectors.toList());
}
@@ -224,6 +246,38 @@ public static PersonnelStatus parseFromString(final String text) {
return STUDENT;
case 5:
return MISSING;
+ case 6:
+ return POW;
+ case 7:
+ return ON_LEAVE;
+ case 8:
+ return AWOL;
+ case 9:
+ return RESIGNED;
+ case 10:
+ return DESERTED;
+ case 11:
+ return DEFECTED;
+ case 12:
+ return HOMICIDE;
+ case 13:
+ return WOUNDS;
+ case 14:
+ return DISEASE;
+ case 15:
+ return ACCIDENTAL;
+ case 16:
+ return NATURAL_CAUSES;
+ case 17:
+ return OLD_AGE;
+ case 18:
+ return MEDICAL_COMPLICATIONS;
+ case 19:
+ return PREGNANCY_COMPLICATIONS;
+ case 20:
+ return UNDETERMINED;
+ case 21:
+ return SUICIDE;
default:
break;
}
diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/PrisonerStatus.java b/MekHQ/src/mekhq/campaign/personnel/enums/PrisonerStatus.java
index 904646d2f2..dfb5455548 100644
--- a/MekHQ/src/mekhq/campaign/personnel/enums/PrisonerStatus.java
+++ b/MekHQ/src/mekhq/campaign/personnel/enums/PrisonerStatus.java
@@ -67,6 +67,9 @@ public String getTitleExtension() {
public boolean isFree() {
return this == FREE;
}
+ public boolean isFreeOrBondsman() {
+ return isFree() || isBondsman();
+ }
public boolean isPrisoner() {
return this == PRISONER;
diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/TurnoverFrequency.java b/MekHQ/src/mekhq/campaign/personnel/enums/TurnoverFrequency.java
new file mode 100644
index 0000000000..2409a6ae50
--- /dev/null
+++ b/MekHQ/src/mekhq/campaign/personnel/enums/TurnoverFrequency.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MekHQ. If not, see .
+ */
+package mekhq.campaign.personnel.enums;
+
+import mekhq.MekHQ;
+
+import java.util.ResourceBundle;
+
+public enum TurnoverFrequency {
+ NEVER("TurnoverFrequency.NEVER.text", "TurnoverFrequency.NEVER.toolTipText"),
+ WEEKLY("TurnoverFrequency.WEEKLY.text", "TurnoverFrequency.WEEKLY.toolTipText"),
+ MONTHLY("TurnoverFrequency.MONTHLY.text", "TurnoverFrequency.MONTHLY.toolTipText"),
+ ANNUALLY("TurnoverFrequency.ANNUALLY.text", "TurnoverFrequency.ANNUALLY.toolTipText");
+
+ private final String name;
+ private final String toolTipText;
+
+ TurnoverFrequency(final String name, final String toolTipText) {
+ final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel",
+ MekHQ.getMHQOptions().getLocale());
+ this.name = resources.getString(name);
+ this.toolTipText = resources.getString(toolTipText);
+ }
+
+ public String getToolTipText() {
+ return toolTipText;
+ }
+
+ public boolean isNever() {
+ return this == NEVER;
+ }
+
+ public boolean isWeekly() {
+ return this == WEEKLY;
+ }
+
+ public boolean isMonthly() {
+ return this == MONTHLY;
+ }
+
+ public boolean isAnnually() {
+ return this == ANNUALLY;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/TurnoverTargetNumberMethod.java b/MekHQ/src/mekhq/campaign/personnel/enums/TurnoverTargetNumberMethod.java
new file mode 100644
index 0000000000..d0a50f6156
--- /dev/null
+++ b/MekHQ/src/mekhq/campaign/personnel/enums/TurnoverTargetNumberMethod.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MekHQ. If not, see .
+ */
+package mekhq.campaign.personnel.enums;
+
+import mekhq.MekHQ;
+
+import java.util.ResourceBundle;
+
+public enum TurnoverTargetNumberMethod {
+ FIXED("TurnoverTargetNumberMethod.FIXED.text", "TurnoverTargetNumberMethod.FIXED.toolTipText"),
+ ADMINISTRATION("TurnoverTargetNumberMethod.ADMINISTRATION.text", "TurnoverTargetNumberMethod.ADMINISTRATION.toolTipText"),
+ NEGOTIATION("TurnoverTargetNumberMethod.NEGOTIATION.text", "TurnoverTargetNumberMethod.NEGOTIATION.toolTipText");
+
+ private final String name;
+ private final String toolTipText;
+
+ TurnoverTargetNumberMethod(final String name, final String toolTipText) {
+ final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel",
+ MekHQ.getMHQOptions().getLocale());
+ this.name = resources.getString(name);
+ this.toolTipText = resources.getString(toolTipText);
+ }
+
+ public String getToolTipText() {
+ return toolTipText;
+ }
+
+ public boolean isFixed() {
+ return this == FIXED;
+ }
+
+ public boolean isAdministration() {
+ return this == ADMINISTRATION;
+ }
+
+ public boolean isNegotiation() {
+ return this == NEGOTIATION;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java
index 71376a4ee4..455cf02b81 100644
--- a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java
+++ b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java
@@ -18,6 +18,7 @@
*/
package mekhq.campaign.personnel.generator;
+import megamek.common.Compute;
import megamek.common.enums.Gender;
import mekhq.campaign.Campaign;
import mekhq.campaign.personnel.Person;
@@ -80,6 +81,8 @@ public Person generate(Campaign campaign, PersonnelRole primaryRole, PersonnelRo
generateBirthday(campaign, person, expLvl, person.isClanPersonnel() && !person.getPhenotype().isNone());
+ person.generateLoyalty(Compute.d6(3));
+
AbstractSkillGenerator skillGenerator = new DefaultSkillGenerator(getSkillPreferences());
skillGenerator.generateSkills(campaign, person, expLvl);
diff --git a/MekHQ/src/mekhq/campaign/personnel/turnoverAndRetention/Fatigue.java b/MekHQ/src/mekhq/campaign/personnel/turnoverAndRetention/Fatigue.java
new file mode 100644
index 0000000000..c9d732db77
--- /dev/null
+++ b/MekHQ/src/mekhq/campaign/personnel/turnoverAndRetention/Fatigue.java
@@ -0,0 +1,148 @@
+package mekhq.campaign.personnel.turnoverAndRetention;
+
+import megamek.common.MiscType;
+import megamek.common.equipment.MiscMounted;
+import mekhq.MekHQ;
+import mekhq.campaign.Campaign;
+import mekhq.campaign.personnel.Person;
+import mekhq.campaign.personnel.enums.PersonnelStatus;
+import mekhq.campaign.unit.Unit;
+
+import java.time.DayOfWeek;
+import java.util.Collection;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.stream.Collectors;
+
+/**
+ * The Fatigue class contains static methods for calculating and processing fatigue levels and actions.
+ */
+public class Fatigue {
+ /**
+ * Calculates the total capacity of field kitchens based on the units present.
+ *
+ * @return The total capacity of field kitchens.
+ */
+ public static Integer checkFieldKitchenCapacity(Campaign campaign) {
+ int fieldKitchenCount = 0;
+
+ Collection allUnits = campaign.getUnits();
+
+ if (!allUnits.isEmpty()) {
+ for (Unit unit : campaign.getUnits()) {
+ if ((unit.isDeployed())
+ || (unit.isDamaged())
+ || (unit.getCrewState().isUncrewed())
+ || (unit.getCrewState().isPartiallyCrewed())
+ || (unit.isUnmaintained())) {
+ continue;
+ }
+
+ List miscItems = unit.getEntity().getMisc();
+
+ if (!miscItems.isEmpty()) {
+ fieldKitchenCount += (int) unit.getEntity().getMisc().stream()
+ .filter(item -> item.getType().hasFlag(MiscType.F_FIELD_KITCHEN))
+ .count();
+ }
+ }
+ }
+
+ return fieldKitchenCount * campaign.getCampaignOptions().getFieldKitchenCapacity();
+ }
+
+
+ /**
+ * Reports the fatigue level of a person and perform actions based on the fatigue level.
+ *
+ * @param person The person for which the fatigue level is reported.
+ */
+ public static void processFatigueActions(Campaign campaign, Person person) {
+ final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Fatigue",
+ MekHQ.getMHQOptions().getLocale());
+
+ int effectiveFatigue = person.getEffectiveFatigue(campaign);
+
+ if (campaign.getCampaignOptions().isUseFatigue()) {
+ if ((effectiveFatigue >= 5) && (effectiveFatigue < 9)) {
+ campaign.addReport(person.getHyperlinkedFullTitle() + ' ' + resources.getString("fatigueTired.text"));
+ person.setIsRecoveringFromFatigue(true);
+ } else if ((effectiveFatigue >= 9) && (effectiveFatigue < 12)) {
+ campaign.addReport(person.getHyperlinkedFullTitle() + ' ' + resources.getString("fatigueFatigued.text"));
+ person.setIsRecoveringFromFatigue(true);
+ } else if ((effectiveFatigue >= 12) && (effectiveFatigue < 16)) {
+ campaign.addReport(person.getHyperlinkedFullTitle() + ' ' + resources.getString("fatigueExhausted.text"));
+ person.setIsRecoveringFromFatigue(true);
+ } else if (effectiveFatigue >= 17) {
+ campaign.addReport(person.getHyperlinkedFullTitle() + ' ' + resources.getString("fatigueCritical.text"));
+ person.setIsRecoveringFromFatigue(true);
+ }
+ }
+
+ if ((campaign.getCampaignOptions().getFatigueLeaveThreshold() != 0)
+ && (effectiveFatigue >= campaign.getCampaignOptions().getFatigueLeaveThreshold())) {
+ person.changeStatus(campaign, campaign.getLocalDate(), PersonnelStatus.ON_LEAVE);
+ }
+ }
+
+ /**
+ * Decreases the fatigue of all active personnel.
+ * Fatigue recovery is determined based on the following criteria:
+ * - Fatigue is adjusted based on various conditions:
+ * - If it is Monday
+ * - Fatigue is decreased by an 1
+ * - If 'person' is on leave, decreased fatigue by an additional 1
+ * - If there are no active contracts, decreased fatigue by an additional 1
+ * - If campaign options include fatigue usage and 'person' is recovering from fatigue:
+ * - If fatigue reaches 0, trigger a report indicating fatigue recovery
+ * - If fatigue leave threshold is not 0, and 'person' is on leave, change status to active
+ */
+ public static void processFatigueRecovery(Campaign campaign) {
+ final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Fatigue",
+ MekHQ.getMHQOptions().getLocale());
+
+ List undepartedPersonnel = campaign.getPersonnel().stream()
+ .filter(person -> !person.getStatus().isDepartedUnit())
+ .collect(Collectors.toList());
+
+ for (Person person : undepartedPersonnel) {
+ if (campaign.getLocalDate().getDayOfWeek().equals(DayOfWeek.MONDAY)) {
+ if (person.getFatigue() > 0) {
+ int fatigueAdjustment = 1;
+
+ if (person.getStatus().isOnLeave()) {
+ fatigueAdjustment++;
+ }
+
+ if (campaign.getActiveContracts().isEmpty()) {
+ fatigueAdjustment++;
+ }
+
+ person.increaseFatigue(- fatigueAdjustment);
+
+ if (person.getFatigue() < 0) {
+ person.setFatigue(0);
+ }
+ }
+ }
+
+ if (campaign.getCampaignOptions().isUseFatigue()) {
+ if ((!person.getStatus().isOnLeave()) && (!person.getIsRecoveringFromFatigue())) {
+ processFatigueActions(campaign, person);
+ }
+
+ if (person.getIsRecoveringFromFatigue()) {
+ if (person.getFatigue() == 0) {
+ campaign.addReport(person.getHyperlinkedFullTitle() + ' ' + resources.getString("fatigueRecovered.text"));
+
+ person.setIsRecoveringFromFatigue(false);
+
+ if ((campaign.getCampaignOptions().getFatigueLeaveThreshold() != 0) && (person.getStatus().isOnLeave())) {
+ person.changeStatus(campaign, campaign.getLocalDate(), PersonnelStatus.ACTIVE);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/MekHQ/src/mekhq/campaign/personnel/turnoverAndRetention/RetirementDefectionTracker.java b/MekHQ/src/mekhq/campaign/personnel/turnoverAndRetention/RetirementDefectionTracker.java
new file mode 100644
index 0000000000..c3e729f989
--- /dev/null
+++ b/MekHQ/src/mekhq/campaign/personnel/turnoverAndRetention/RetirementDefectionTracker.java
@@ -0,0 +1,1170 @@
+/*
+ * RetirementDefectionTracker.java
+ *
+ * Copyright (c) 2014 - Carl Spain. All rights reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MekHQ. If not, see .
+ */
+package mekhq.campaign.personnel.turnoverAndRetention;
+
+import megamek.codeUtilities.MathUtility;
+import megamek.common.Compute;
+import megamek.common.TargetRoll;
+import megamek.common.annotations.Nullable;
+import megamek.common.options.IOption;
+import mekhq.Utilities;
+import mekhq.campaign.Campaign;
+import mekhq.campaign.finances.FinancialReport;
+import mekhq.campaign.finances.Money;
+import mekhq.campaign.mission.AtBContract;
+import mekhq.campaign.mission.Mission;
+import mekhq.campaign.personnel.Injury;
+import mekhq.campaign.personnel.Person;
+import mekhq.campaign.personnel.PersonnelOptions;
+import mekhq.campaign.personnel.SkillType;
+import mekhq.campaign.personnel.enums.PersonnelRole;
+import mekhq.campaign.personnel.enums.Profession;
+import mekhq.campaign.universe.FactionHints;
+import mekhq.utilities.MHQXMLUtility;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintWriter;
+import java.time.LocalDate;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker.Payout.isBreakingContract;
+
+/**
+ * @author Neoancient
+ *
+ * Against the Bot
+ * Utility class that handles Employee Turnover rolls and final payments
+ * to personnel who retire/defect/get sacked and families of those killed
+ * in battle.
+ */
+public class RetirementDefectionTracker {
+ /* In case the dialog is closed after making the retirement rolls
+ * and determining payouts, but before the retirees have been paid,
+ * we store those results to avoid making the rolls again.
+ */
+ final private Set rollRequired;
+ final private Map> unresolvedPersonnel;
+ final private Map payouts;
+ private LocalDate lastRetirementRoll;
+
+ private static Person asfCommander;
+ private static Integer asfCommanderModifier;
+ private static Person vehicleCrewCommander;
+ private static Integer vehicleCrewCommanderModifier;
+ private static Person infantryCommander;
+ private static Integer infantryCommanderModifier;
+ private static Person navalCommander;
+ private static Integer navalCommanderModifier;
+ private static Person techCommander;
+ private static Integer techCommanderModifier;
+ private static Person medicalCommander;
+ private static Integer medicalCommanderModifier;
+ private static Person administrationCommander;
+ private static Integer administrationCommanderModifier;
+ private static Person mechWarriorCommander;
+ private static Integer mechWarriorCommanderModifier;
+
+ private Integer hrSkill;
+ private Integer difficulty;
+
+ private final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.RetirementDefectionTracker");
+
+ public RetirementDefectionTracker() {
+ rollRequired = new HashSet<>();
+ unresolvedPersonnel = new HashMap<>();
+ payouts = new HashMap<>();
+ lastRetirementRoll = LocalDate.now();
+ }
+
+ /**
+ * Computes the target for retirement rolls for all eligible personnel; this includes
+ * all active personnel who are not dependents, prisoners, or bondsmen.
+ *
+ * @param mission The contract that is being resolved; if the retirement roll is not due to
+ * contract resolutions (e.g., > 12 months since last roll), this can be null.
+ * @param campaign The campaign to calculate target numbers for
+ * @return A map with person ids as key and calculated target roll as value.
+ */
+ public Map getTargetNumbers(final @Nullable Mission mission, final Campaign campaign) {
+ final Map targets = new HashMap<>();
+
+ if (null != mission) {
+ rollRequired.add(mission.getId());
+ }
+
+ if (!campaign.getCampaignOptions().getTurnoverTargetNumberMethod().isFixed()) {
+ setHrSkill(campaign);
+ getDifficultyModifier(campaign);
+ }
+
+ if (campaign.getCampaignOptions().isUseManagementSkill()) {
+ getManagementSkillValues(campaign);
+ }
+
+ for (Person person : campaign.getActivePersonnel()) {
+ if ((person.getPrimaryRole().isCivilian()) || (!person.getPrisonerStatus().isFree()) || (person.isDeployed())) {
+ continue;
+ }
+
+ if (person.isFounder()) {
+ if (person.getAge(campaign.getLocalDate()) < 50) {
+ if (!campaign.getCampaignOptions().isUseRandomFounderTurnover()) {
+ continue;
+ }
+ } else {
+ if (!campaign.getCampaignOptions().isUseFounderRetirement()) {
+ continue;
+ }
+ }
+ }
+
+ if (campaign.getCampaignOptions().isUseSubContractSoldiers()) {
+ if ((person.getUnit() != null) && (person.getUnit().usesSoldiers()) && (!person.getUnit().isCommander(person))) {
+ continue;
+ }
+ }
+
+ TargetRoll targetNumber = new TargetRoll(getBaseTargetNumber(campaign, person), resources.getString("base.text"));
+
+ // Founder Modifier
+ if (person.isFounder()) {
+ targetNumber.addModifier(-2, resources.getString("founder.text"));
+ }
+
+ // Service Contract
+ if (isBreakingContract(person, campaign.getLocalDate(), campaign.getCampaignOptions().getServiceContractDuration())) {
+ targetNumber.addModifier(-campaign.getCampaignOptions().getServiceContractModifier(), resources.getString("contract.text"));
+ }
+
+ // Desirability modifier
+ if ((campaign.getCampaignOptions().isUseSkillModifiers()) && (person.getAge(campaign.getLocalDate()) < 50)) {
+ targetNumber.addModifier(person.getExperienceLevel(campaign, false), resources.getString("desirability.text"));
+ }
+
+ // Fatigue modifier
+ if ((campaign.getCampaignOptions().isUseFatigue()) && (campaign.getCampaignOptions().isUseFatigueModifiers())) {
+ int fatigueModifier = MathUtility.clamp(((person.getFatigue() - 1) / 4) - 1, 0, 3);
+
+ if (fatigueModifier > 0) {
+ targetNumber.addModifier(fatigueModifier, resources.getString("fatigue.text"));
+ }
+ }
+
+ // Administrative Strain Modifiers
+ if (campaign.getCampaignOptions().isUseAdministrativeStrain()) {
+ int administrativeStrainModifier = getAdministrativeStrainModifier(campaign);
+
+ if (administrativeStrainModifier > 0) {
+ targetNumber.addModifier(administrativeStrainModifier, resources.getString("administrativeStrain.text"));
+ }
+ }
+
+ // Management Skill Modifier
+ if (campaign.getCampaignOptions().isUseManagementSkill()) {
+ targetNumber.addModifier(getManagementSkillModifier(person), resources.getString("managementSkill.text"));
+ }
+
+ // Shares Modifiers
+ if (campaign.getCampaignOptions().isUseShareSystem()) {
+ // If this retirement roll is not being made at the end of a contract (e.g. >12 months since last roll),
+ // the share percentage should still apply.
+ // In the case of multiple active contracts, pick the one with the best percentage.
+
+ AtBContract contract;
+
+ try {
+ contract = (AtBContract) mission;
+ } catch (Exception e) {
+ contract = null;
+ }
+
+ if (contract == null) {
+ List atbContracts = campaign.getActiveAtBContracts();
+
+ if (!atbContracts.isEmpty()) {
+ for (AtBContract atbContract : atbContracts) {
+ if ((contract == null) || (contract.getSharesPct() < atbContract.getSharesPct())) {
+ contract = atbContract;
+ }
+ }
+ }
+ }
+
+ if (contract != null) {
+ targetNumber.addModifier(- Math.max(0, ((contract.getSharesPct() / 10) - 2)), resources.getString("shares.text"));
+ }
+ }
+
+ // Unit Rating modifier
+ if (campaign.getCampaignOptions().isUseUnitRatingModifiers()) {
+ int unitRatingModifier = getUnitRatingModifier(campaign);
+ targetNumber.addModifier(unitRatingModifier, resources.getString("unitRating.text"));
+ }
+
+ // Mission completion status modifiers
+ if ((mission != null) && (campaign.getCampaignOptions().isUseMissionStatusModifiers())) {
+ if (mission.getStatus().isSuccess()) {
+ targetNumber.addModifier(-1, resources.getString("missionSuccess.text"));
+ } else if (mission.getStatus().isFailed()) {
+ targetNumber.addModifier(1, resources.getString("missionFailure.text"));
+ } else if (mission.getStatus().isBreach()) {
+ targetNumber.addModifier(2, resources.getString("missionBreach.text"));
+ }
+ }
+
+ // Loyalty
+ if ((campaign.getCampaignOptions().isUseLoyaltyModifiers())
+ && (!campaign.getCampaignOptions().isUseHideLoyalty())
+ && (person.getLoyalty() != 0)) {
+ targetNumber.addModifier(-person.getLoyalty(), resources.getString("loyalty.text"));
+ }
+
+ // Faction Modifiers
+ if (campaign.getCampaignOptions().isUseFactionModifiers()) {
+ if (campaign.getFaction().isPirate()) {
+ targetNumber.addModifier(1, resources.getString("factionPirateCompany.text"));
+ } else if (person.getOriginFaction().isPirate()) {
+ targetNumber.addModifier(1, resources.getString("factionPirate.text"));
+ }
+
+ if (person.getOriginFaction().isMercenary()) {
+ targetNumber.addModifier(1, resources.getString("factionMercenary.text"));
+ }
+
+ if (person.getOriginFaction().isClan()) {
+ targetNumber.addModifier(-2, resources.getString("factionClan.text"));
+ }
+
+ if (FactionHints.defaultFactionHints().isAtWarWith(campaign.getFaction(), person.getOriginFaction(), campaign.getLocalDate())) {
+ targetNumber.addModifier(2, resources.getString("factionEnemy.text"));
+ }
+ }
+
+ // Age Modifiers
+ if (campaign.getCampaignOptions().isUseAgeModifiers()) {
+ int ageMod = getAgeMod(person.getAge(campaign.getLocalDate()));
+
+ if (ageMod < 0) {
+ targetNumber.addModifier(ageMod, resources.getString("ageYoung.text"));
+ } else if ((ageMod > 0) && (!isBreakingContract(person, campaign.getLocalDate(), campaign.getCampaignOptions().getServiceContractDuration()))) {
+ targetNumber.addModifier(ageMod, resources.getString("ageRetirement.text"));
+ }
+ }
+
+ // Family Modifier
+ if (campaign.getCampaignOptions().isUseMarriageModifiers()) {
+ Person spouse = person.getGenealogy().getSpouse();
+
+ if ((spouse != null) && (!spouse.getPrimaryRole().isCivilian())) {
+ targetNumber.addModifier(-2, resources.getString("marriage.text"));
+ }
+ }
+
+ // Injury Modifiers
+ int injuryMod = (int) person.getInjuries()
+ .stream()
+ .filter(Injury::isPermanent).count();
+
+ if (injuryMod > 0) {
+ targetNumber.addModifier(injuryMod, resources.getString("injuries.text"));
+ }
+
+ // Officer Modifiers
+ if (person.getRank().isOfficer()) {
+ targetNumber.addModifier(-1, resources.getString("officer.text"));
+ } else {
+ for (Enumeration i = person.getOptions(PersonnelOptions.LVL3_ADVANTAGES); i.hasMoreElements(); ) {
+ IOption ability = i.nextElement();
+ if (ability.booleanValue()) {
+ if (ability.getName().equals("tactical_genius")) {
+ targetNumber.addModifier(1, resources.getString("tacticalGenius.text"));
+ break;
+ }
+ }
+ }
+ }
+
+ targets.put(person.getId(), targetNumber);
+ }
+ return targets;
+ }
+
+ /**
+ * Sets the HR skill averaged across all Admin/HR personnel.
+ *
+ * @param campaign the Campaign object to get personnel from.
+ */
+ private void setHrSkill(Campaign campaign) {
+ int hrPersonnelCount = (int) campaign.getActivePersonnel().stream()
+ .filter(person -> (!person.getPrisonerStatus().isPrisoner()) && (!person.getPrisonerStatus().isPrisonerDefector()))
+ .filter(person -> (person.getPrimaryRole().isAdministratorHR()) || (person.getSecondaryRole().isAdministratorHR()))
+ .count();
+
+ if (hrPersonnelCount != 0) {
+ if (campaign.getCampaignOptions().getTurnoverTargetNumberMethod().isNegotiation()) {
+ hrSkill = getCombinedSkillValues(campaign, SkillType.S_NEG) / hrPersonnelCount;
+ } else if (campaign.getCampaignOptions().getTurnoverTargetNumberMethod().isAdministration()) {
+ hrSkill = getCombinedSkillValues(campaign, SkillType.S_ADMIN) / hrPersonnelCount;
+ }
+ } else {
+ hrSkill = 0;
+ }
+ }
+
+ /**
+ * Calculates the management skill modifier for a person
+ *
+ * @param person the individual we're fetching the modifier for
+ * @return the management skill modifier
+ */
+ private static int getManagementSkillModifier(Person person) {
+ if ((person.getPrimaryRole().isCivilian()) || (!person.getPrisonerStatus().isFree())) {
+ return 0;
+ }
+
+ if (person.getSecondaryRole() == PersonnelRole.NONE) {
+ return -getCommanderManagementSkill(person.getPrimaryRole());
+ } else {
+ return -((getCommanderManagementSkill(person.getPrimaryRole()) + getCommanderManagementSkill(person.getSecondaryRole())) / 2);
+ }
+ }
+
+ /**
+ * Returns the management skill modifier for a commander based on the given personnel role.
+ *
+ * @param role the personnel role of the person we're fetching the modifier for
+ * @return the management skill modifier for the commander
+ */
+ private static int getCommanderManagementSkill(PersonnelRole role) {
+ switch (Profession.getProfessionFromPersonnelRole(role)) {
+ case AEROSPACE:
+ return asfCommanderModifier;
+ case VEHICLE:
+ return vehicleCrewCommanderModifier;
+ case INFANTRY:
+ return infantryCommanderModifier;
+ case NAVAL:
+ return navalCommanderModifier;
+ case TECH:
+ return techCommanderModifier;
+ case MEDICAL:
+ return medicalCommanderModifier;
+ case ADMINISTRATOR:
+ return administrationCommanderModifier;
+ case MECHWARRIOR:
+ return mechWarriorCommanderModifier;
+ case CIVILIAN:
+ return 0;
+ }
+ return 0;
+ }
+
+ /**
+ * This method calculates the management skill values for the different commanding officers.
+ * Each commander's management skill value is calculated based on their role and rank within the campaign.
+ * The management skill modifier is calculated by adding the base modifier
+ * (retrieved from campaign options) and the commander's individual leadership skill.
+ * If no suitable commander is found for a particular role,
+ * the management skill modifier for that role remains the same as the base modifier.
+ *
+ * @param campaign The Campaign object for which to calculate the management skill values.
+ */
+ private void getManagementSkillValues(Campaign campaign) {
+ int baseModifier = campaign.getCampaignOptions().getManagementSkillPenalty();
+
+ if (campaign.getCampaignOptions().isUseCommanderLeadershipOnly()) {
+ Person commander = campaign.getFlaggedCommander();
+
+ if ((commander != null) && (commander.hasSkill(SkillType.S_LEADER))) {
+ int commanderSkill = baseModifier + commander.getSkill(SkillType.S_LEADER).getFinalSkillValue();
+
+ asfCommanderModifier = commanderSkill;
+ vehicleCrewCommanderModifier = commanderSkill;
+ infantryCommanderModifier = commanderSkill;
+ navalCommanderModifier = commanderSkill;
+ techCommanderModifier = commanderSkill;
+ medicalCommanderModifier = commanderSkill;
+ administrationCommanderModifier = commanderSkill;
+ mechWarriorCommanderModifier = commanderSkill;
+ } else {
+ asfCommanderModifier = baseModifier;
+ vehicleCrewCommanderModifier = baseModifier;
+ infantryCommanderModifier = baseModifier;
+ navalCommanderModifier = baseModifier;
+ techCommanderModifier = baseModifier;
+ medicalCommanderModifier = baseModifier;
+ administrationCommanderModifier = baseModifier;
+ mechWarriorCommanderModifier = baseModifier;
+ }
+
+ return;
+ }
+
+ for (Person person : campaign.getActivePersonnel()) {
+ if ((person.getPrimaryRole().isCivilian())
+ || (person.getPrisonerStatus().isPrisoner())
+ || (person.getPrisonerStatus().isPrisonerDefector())) {
+ continue;
+ }
+
+ switch (Profession.getProfessionFromPersonnelRole(person.getPrimaryRole())) {
+ case AEROSPACE:
+ if (person.outRanksUsingSkillTiebreaker(campaign, asfCommander)) {
+ asfCommander = person;
+ asfCommanderModifier = baseModifier + getIndividualCommanderLeadership(asfCommander);
+ }
+ break;
+ case VEHICLE:
+ if (person.outRanksUsingSkillTiebreaker(campaign, vehicleCrewCommander)) {
+ vehicleCrewCommander = person;
+ vehicleCrewCommanderModifier = baseModifier + getIndividualCommanderLeadership(vehicleCrewCommander);
+ }
+ break;
+ case INFANTRY:
+ if (person.outRanksUsingSkillTiebreaker(campaign, infantryCommander)) {
+ infantryCommander = person;
+ infantryCommanderModifier = baseModifier + getIndividualCommanderLeadership(infantryCommander);
+ }
+ break;
+ case NAVAL:
+ if (person.outRanksUsingSkillTiebreaker(campaign, navalCommander)) {
+ navalCommander = person;
+ navalCommanderModifier = baseModifier + getIndividualCommanderLeadership(navalCommander);
+ }
+ break;
+ case TECH:
+ if (person.outRanksUsingSkillTiebreaker(campaign, techCommander)) {
+ techCommander = person;
+ techCommanderModifier = baseModifier + getIndividualCommanderLeadership(techCommander);
+ }
+ break;
+ case MEDICAL:
+ if (person.outRanksUsingSkillTiebreaker(campaign, medicalCommander)) {
+ medicalCommander = person;
+ medicalCommanderModifier = baseModifier + getIndividualCommanderLeadership(medicalCommander);
+ }
+ break;
+ case ADMINISTRATOR:
+ if (person.outRanksUsingSkillTiebreaker(campaign, administrationCommander)) {
+ administrationCommander = person;
+ administrationCommanderModifier = baseModifier + getIndividualCommanderLeadership(administrationCommander);
+ }
+ break;
+ case MECHWARRIOR:
+ if (person.outRanksUsingSkillTiebreaker(campaign, mechWarriorCommander)) {
+ mechWarriorCommander = person;
+ mechWarriorCommanderModifier = baseModifier + getIndividualCommanderLeadership(mechWarriorCommander);
+ }
+ break;
+ case CIVILIAN:
+ break;
+ }
+ }
+
+ for (Profession profession : Profession.values()) {
+ switch (profession) {
+ case AEROSPACE:
+ if (asfCommander == null) {
+ asfCommanderModifier = baseModifier;
+ }
+ break;
+ case VEHICLE:
+ if (vehicleCrewCommander == null) {
+ vehicleCrewCommanderModifier = baseModifier;
+ }
+ break;
+ case INFANTRY:
+ if (infantryCommander == null) {
+ infantryCommanderModifier = baseModifier;
+ }
+ break;
+ case NAVAL:
+ if (navalCommander == null) {
+ navalCommanderModifier = baseModifier;
+ }
+ break;
+ case TECH:
+ if (techCommander == null) {
+ techCommanderModifier = baseModifier;
+ }
+ break;
+ case MEDICAL:
+ if (medicalCommander == null) {
+ medicalCommanderModifier = baseModifier;
+ }
+ break;
+ case ADMINISTRATOR:
+ if (administrationCommander == null) {
+ administrationCommanderModifier = baseModifier;
+ }
+ break;
+ case MECHWARRIOR:
+ if (mechWarriorCommander == null) {
+ mechWarriorCommanderModifier = baseModifier;
+ }
+ break;
+ case CIVILIAN:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Calculates the individual commander Leadership skill based on the provided commander.
+ *
+ * @param commander the commander for which the skill is being calculated
+ * @return the Leadership skill
+ */
+ private static int getIndividualCommanderLeadership(Person commander) {
+ if (commander.hasSkill(SkillType.S_LEADER)) {
+ return commander.getSkill(SkillType.S_LEADER).getFinalSkillValue();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * This method calculates the combatant strain modifier based on the active personnel assigned to units.
+ *
+ * @param campaign the campaign for which to calculate the strain modifier
+ * @return the strain modifier
+ */
+ public static int getAdministrativeStrainModifier(Campaign campaign) {
+ int personnel = getAdministrativeStrain(campaign);
+
+ int maximumStrain = campaign.getCampaignOptions().getAdministrativeCapacity() * getCombinedSkillValues(campaign, SkillType.S_ADMIN);
+
+ if (maximumStrain != 0) {
+ return personnel / maximumStrain;
+ } else {
+ return personnel;
+ }
+ }
+
+ /**
+ * Calculates the administrative strain for a given campaign.
+ *
+ * @param campaign the campaign for which to calculate the administrative strain
+ * @return the total administrative strain of the campaign
+ */
+ public static int getAdministrativeStrain(Campaign campaign) {
+ int personnel = 0;
+ int proto = 0;
+ int multiCrew = 0;
+ int other = 0;
+
+ for (Person person : campaign.getActivePersonnel()) {
+ if ((person.getPrimaryRole().isCivilian()) || (person.getPrisonerStatus().isPrisoner()) || (person.getPrisonerStatus().isPrisonerDefector())) {
+ other++;
+ } else if (person.getUnit() != null) {
+ if (person.getUnit().isCommander(person)) {
+ if (person.getUnit().getEntity().isProtoMek()) {
+ proto++;
+ } else {
+ multiCrew++;
+ }
+ }
+ } else {
+ if ((person.getPrimaryRole().isAstech()) && person.getSecondaryRole().isNone()) {
+ continue;
+ } else if ((person.getPrimaryRole().isMedic()) && person.getSecondaryRole().isNone()) {
+ continue;
+ } else if ((person.getPrimaryRole().isMedic()) && person.getSecondaryRole().isAstech()) {
+ continue;
+ } else if ((person.getPrimaryRole().isAstech()) && person.getSecondaryRole().isMedic()) {
+ continue;
+ }
+
+ personnel++;
+ }
+ }
+
+ personnel += proto / 5;
+ personnel += multiCrew / campaign.getCampaignOptions().getMultiCrewStrainDivider();
+ personnel += other / (campaign.getCampaignOptions().getMultiCrewStrainDivider() * 2);
+
+ return personnel;
+ }
+
+ /**
+ * Calculates the combined skill values of active Admin/HR personnel.
+ *
+ * @param campaign the campaign for which to calculate the combined skill values
+ * @return the combined skill values of active Admin/HR personnel in the campaign
+ */
+ public static int getCombinedSkillValues(Campaign campaign, String skillType) {
+ int combinedSkillValues = 0;
+
+ for (Person person : campaign.getActivePersonnel()) {
+ if ((!person.getPrisonerStatus().isPrisoner()) || (!person.getPrisonerStatus().isPrisonerDefector())) {
+ if (person.getPrimaryRole().isAdministratorHR()) {
+ if (person.hasSkill(skillType)) {
+ combinedSkillValues += person.getSkill(skillType).getLevel();
+ combinedSkillValues += person.getSkill(skillType).getBonus();
+ }
+ } else if (person.getSecondaryRole().isAdministratorHR()) {
+ if (person.hasSkill(skillType)) {
+ combinedSkillValues += person.getSkill(skillType).getLevel();
+ combinedSkillValues += person.getSkill(skillType).getBonus();
+ }
+ }
+ }
+ }
+ return combinedSkillValues;
+ }
+
+ /**
+ * Returns a difficulty modifier based on the turnover difficulty campaign setting.
+ *
+ * @param campaign the current campaign
+ */
+ private void getDifficultyModifier(Campaign campaign) {
+ switch (campaign.getCampaignOptions().getTurnoverDifficulty()) {
+ case NONE:
+ difficulty = -3;
+ break;
+ case ULTRA_GREEN:
+ difficulty = -2;
+ break;
+ case GREEN:
+ difficulty = -1;
+ break;
+ case REGULAR:
+ difficulty = 0;
+ break;
+ case VETERAN:
+ difficulty = 1;
+ break;
+ case ELITE:
+ difficulty = 2;
+ break;
+ case HEROIC:
+ difficulty = 3;
+ break;
+ case LEGENDARY:
+ difficulty = 4;
+ break;
+ }
+ }
+
+ /**
+ * This method calculates the base target number.
+ *
+ * @param campaign the campaign for which the base target number is calculated
+ * @return the base target number
+ */
+ private int getBaseTargetNumber(Campaign campaign, Person person) {
+ if (!campaign.getCampaignOptions().getTurnoverTargetNumberMethod().isFixed()) {
+ int targetNumber;
+
+ // we use 'shellPerson' as we have no way to ensure 'person' has the necessary skills, and we'll get an NPE if they don't
+ Person shellPerson = new Person(campaign);
+
+ if (campaign.getCampaignOptions().getTurnoverTargetNumberMethod().isNegotiation()) {
+ shellPerson.addSkill(SkillType.S_NEG, 1, 0);
+ targetNumber = shellPerson.getSkills().getSkill(SkillType.S_NEG).getType().getTarget();
+ } else {
+ shellPerson.addSkill(SkillType.S_ADMIN, 1, 0);
+ targetNumber = shellPerson.getSkills().getSkill(SkillType.S_ADMIN).getType().getTarget();
+ }
+
+ if (campaign.getCampaignOptions().getServiceContractDuration() == 0) {
+ targetNumber -= campaign.getCampaignOptions().getServiceContractModifier();
+ }
+
+ if ((campaign.getCampaignOptions().isUseLoyaltyModifiers()) && (campaign.getCampaignOptions().isUseHideLoyalty())) {
+ return targetNumber - hrSkill + difficulty - person.getLoyalty();
+ } else {
+ return targetNumber - hrSkill + difficulty;
+ }
+ } else {
+ if ((campaign.getCampaignOptions().isUseLoyaltyModifiers()) && (campaign.getCampaignOptions().isUseHideLoyalty())) {
+ return campaign.getCampaignOptions().getTurnoverFixedTargetNumber() - person.getLoyalty();
+ } else {
+ return campaign.getCampaignOptions().getTurnoverFixedTargetNumber();
+ }
+ }
+ }
+
+ /**
+ * Returns the unit rating modifier for the campaign.
+ *
+ * @param campaign the campaign from which to derive the unit rating modifier
+ * @return the unit rating modifier
+ */
+ private static int getUnitRatingModifier(Campaign campaign) {
+ int unitRating = 0;
+
+ if (campaign.getUnitRatingMod() < 1) {
+ unitRating = 2;
+ } else if (campaign.getUnitRatingMod() == 1) {
+ unitRating = 1;
+ } else if (campaign.getUnitRatingMod() > 3) {
+ unitRating = -1;
+ }
+ return unitRating;
+ }
+
+ /**
+ * @param campaign the campaign to get share values for
+ * @return The value of each share in C-bills
+ */
+ public static Money getShareValue(Campaign campaign) {
+ if (!campaign.getCampaignOptions().isUseShareSystem()) {
+ return Money.zero();
+ }
+
+ FinancialReport r = FinancialReport.calculate(campaign);
+
+ Money netWorth = r.getNetWorth();
+ if (campaign.getCampaignOptions().isSharesExcludeLargeCraft()) {
+ netWorth = netWorth.minus(r.getLargeCraftValue());
+ }
+
+ int totalShares = campaign.getActivePersonnel()
+ .stream()
+ .mapToInt(p -> p.getNumShares(campaign, campaign.getCampaignOptions().isSharesForAll()))
+ .sum();
+
+ if (totalShares <= 0) {
+ return Money.zero();
+ }
+
+ return netWorth.dividedBy(totalShares);
+ }
+
+ /**
+ * @param age the age of the employee
+ * @return the age-based modifier
+ */
+ private static int getAgeMod(int age) {
+ int ageMod = 0;
+
+ if (age <= 20) {
+ ageMod = -1;
+ } else if ((age >= 50) && (age < 65)) {
+ ageMod = 3;
+ } else if ((age >= 65) && (age < 75)) {
+ ageMod = 4;
+ } else if ((age >= 75) && (age < 85)) {
+ ageMod = 5;
+ } else if ((age >= 85) && (age < 95)) {
+ ageMod = 6;
+ } else if ((age >= 95) && (age < 105)) {
+ ageMod = 7;
+ } else if (age >= 105) {
+ ageMod = 8;
+ }
+
+ return ageMod;
+ }
+
+ /**
+ * Makes rolls for Employee Turnover based on previously calculated target rolls,
+ * and tracks all retirees in the unresolvedPersonnel hash in case the dialog
+ * is closed before payments are resolved, to avoid re-rolling the results.
+ *
+ * @param mission Nullable mission value
+ * @param targets The hash previously generated by getTargetNumbers.
+ * @param shareValue The value of each share in the unit; if not using the share system, this is zero.
+ * @param campaign the current campaign
+ */
+ public void rollRetirement(final @Nullable Mission mission, final Map targets,
+ final Money shareValue, final Campaign campaign) {
+ if ((mission != null) && !unresolvedPersonnel.containsKey(mission.getId())) {
+ unresolvedPersonnel.put(mission.getId(), new HashSet<>());
+ }
+
+ for (UUID id : targets.keySet()) {
+ if (Compute.d6(2) < targets.get(id).getValue()) {
+ if (mission != null) {
+ unresolvedPersonnel.get(mission.getId()).add(id);
+ }
+
+ Person p = campaign.getPerson(id);
+
+ // if the retiree is the commander of an infantry platoon, all non-founders in the platoon follow them into retirement
+ if (campaign.getCampaignOptions().isUseSubContractSoldiers()) {
+ if ((p.getUnit() != null) && (p.getUnit().usesSoldiers()) && (p.getUnit().isCommander(p))) {
+ for (Person person : p.getUnit().getSoldiers()) {
+ if ((!person.isFounder()) || (campaign.getCampaignOptions().isUseRandomFounderTurnover())) {
+ payouts.put(person.getId(), new Payout(campaign, campaign.getPerson(person.getId()),
+ shareValue, false, campaign.getCampaignOptions().isSharesForAll()));
+ }
+ }
+
+ continue;
+ }
+ }
+
+ payouts.put(id, new Payout(campaign, campaign.getPerson(id),
+ shareValue, false, campaign.getCampaignOptions().isSharesForAll()));
+ }
+ }
+
+ if (mission != null) {
+ rollRequired.remove(mission.getId());
+ }
+
+ lastRetirementRoll = campaign.getLocalDate();
+ }
+
+ public LocalDate getLastRetirementRoll() {
+ return lastRetirementRoll;
+ }
+
+ public void setLastRetirementRoll(LocalDate lastRetirementRoll) {
+ this.lastRetirementRoll = lastRetirementRoll;
+ }
+
+ /**
+ * Handles final payout to any personnel who are sacked or killed in battle
+ *
+ * @param person The person to be removed from the campaign
+ * @param killed True if killed in battle, false if sacked
+ * @param campaign the ongoing campaign
+ * @param contract If not null, the payout must be resolved before the contract can be resolved.
+ * @return true, if the person is due a payout, otherwise false
+ */
+ public boolean removeFromCampaign(Person person, boolean killed, Campaign campaign,
+ AtBContract contract) {
+ if (!person.getPrisonerStatus().isFree()) {
+ return false;
+ }
+
+ payouts.put(person.getId(), new Payout(campaign, person, getShareValue(campaign),
+ killed, campaign.getCampaignOptions().isSharesForAll()));
+
+ if (null != contract) {
+ unresolvedPersonnel.computeIfAbsent(contract.getId(), k -> new HashSet<>());
+ unresolvedPersonnel.get(contract.getId()).add(person.getId());
+ }
+
+ return true;
+ }
+
+ public void removePayout(Person person) {
+ payouts.remove(person.getId());
+ }
+
+ /**
+ * Clears out an individual entirely from this tracker.
+ * @param person The person to remove
+ */
+ public void removePerson(Person person) {
+ payouts.remove(person.getId());
+
+ for (int contractID : unresolvedPersonnel.keySet()) {
+ unresolvedPersonnel.get(contractID).remove(person.getId());
+ }
+ }
+
+ /**
+ * Worker function that clears out any orphan Employee Turnover records
+ */
+ public void cleanupOrphans(Campaign campaign) {
+ payouts.keySet().removeIf(personID -> campaign.getPerson(personID) == null);
+
+ for (int contractID : unresolvedPersonnel.keySet()) {
+ unresolvedPersonnel.get(contractID).removeIf(personID -> campaign.getPerson(personID) == null);
+ }
+ }
+
+ public boolean isOutstanding(int id) {
+ return unresolvedPersonnel.containsKey(id);
+ }
+
+ /** Called by when all payouts have been resolved for the contract.
+ * If the contract is null, the dialog has been invoked without a
+ * specific contract and all outstanding payouts have been resolved.
+ */
+ public void resolveAllContracts() {
+ resolveContract(null);
+ payouts.clear();
+ }
+
+ public void resolveContract(final @Nullable Mission mission) {
+ if (mission == null) {
+ unresolvedPersonnel.keySet().forEach(this::resolveContract);
+ unresolvedPersonnel.clear();
+ } else {
+ resolveContract(mission.getId());
+ unresolvedPersonnel.remove(mission.getId());
+ }
+ }
+
+ private void resolveContract(int contractId) {
+ if (null != unresolvedPersonnel.get(contractId)) {
+ for (UUID pid : unresolvedPersonnel.get(contractId)) {
+ payouts.remove(pid);
+ }
+ }
+ rollRequired.remove(contractId);
+ }
+
+ public Set getRetirees() {
+ return getRetirees(null);
+ }
+
+ public Set getRetirees(final @Nullable Mission mission) {
+ return (mission == null) ? payouts.keySet() : unresolvedPersonnel.get(mission.getId());
+ }
+
+ public Payout getPayout(UUID id) {
+ return payouts.get(id);
+ }
+
+ /**
+ * @param campaign the campaign the person is a part of
+ * @param person the person to get the bonus cost for
+ * @return The amount in C-bills required to get a bonus to the Employee Turnover roll
+ */
+ public static Money getPayoutOrBonusValue(final Campaign campaign, Person person) {
+ double bonusMultiplier = campaign.getCampaignOptions().getPayoutRateEnlisted();
+
+ if (person.getRank().isOfficer()) {
+ bonusMultiplier = campaign.getCampaignOptions().getPayoutRateOfficer();
+ }
+
+ if (campaign.getCampaignOptions().isUsePayoutServiceBonus()) {
+ bonusMultiplier += person.getYearsInService(campaign) * ((double) campaign.getCampaignOptions().getPayoutServiceBonusRate() / 100);
+ }
+
+ return person.getSalary(campaign).multipliedBy(bonusMultiplier);
+ }
+
+ /**
+ * Class used to record the required payout to each retired/defected/killed/sacked
+ * person.
+ */
+ public static class Payout {
+ private int weightClass = 0;
+ private Money payoutAmount = Money.zero();
+ private boolean stolenUnit = false;
+ private UUID stolenUnitId = null;
+
+ public Payout() {
+
+ }
+
+ public Payout(final Campaign campaign, final Person person, final Money shareValue, final boolean killed, final boolean sharesForAll) {
+ calculatePayout(campaign, person, killed, shareValue.isPositive());
+
+ if ((shareValue.isPositive()) && (campaign.getCampaignOptions().isUseShareSystem())) {
+ payoutAmount = payoutAmount.plus(shareValue.multipliedBy(person.getNumShares(campaign, sharesForAll)));
+ }
+ }
+
+ private void calculatePayout(final Campaign campaign, final Person person, final boolean killed, final boolean shareSystem) {
+ int roll;
+
+ if (killed) {
+ roll = Utilities.dice(1, 5);
+ } else {
+ roll = Compute.d6() + Math.max(-1, person.getExperienceLevel(campaign, false) - 2);
+ if (person.getRank().isOfficer()) {
+ roll += 1;
+ }
+ }
+
+ if (roll >= 6 && (person.getPrimaryRole().isAerospacePilot() || person.getSecondaryRole().isAerospacePilot())) {
+ stolenUnit = true;
+ } else {
+ final Profession profession = Profession.getProfessionFromPersonnelRole(person.getPrimaryRole());
+
+ // person is defecting
+ if (isBreakingContract(person, campaign.getLocalDate(), campaign.getCampaignOptions().getServiceContractDuration())) {
+ payoutAmount = Money.of(0);
+ // person is retiring
+ } else if (person.getAge(campaign.getLocalDate()) >= 50) {
+ payoutAmount = getPayoutOrBonusValue(campaign, person).multipliedBy(campaign.getCampaignOptions().getPayoutRetirementMultiplier());
+ // person is resigning
+ } else {
+ payoutAmount = getPayoutOrBonusValue(campaign, person);
+ }
+
+ if (!shareSystem && (profession.isMechWarrior() || profession.isAerospace())
+ && (person.getOriginalUnitWeight() > 0)) {
+ weightClass = person.getOriginalUnitWeight() + person.getOriginalUnitTech();
+ }
+ }
+ }
+
+ public int getWeightClass() {
+ return weightClass;
+ }
+
+ public void setWeightClass(int weight) {
+ weightClass = weight;
+ }
+
+
+ public Money getPayoutAmount() {
+ return payoutAmount;
+ }
+
+ public void setPayoutAmount(Money payoutAmount) {
+ this.payoutAmount = payoutAmount;
+ }
+
+ public boolean hasStolenUnit() {
+ return stolenUnit;
+ }
+
+ public void setStolenUnit(boolean stolen) {
+ stolenUnit = stolen;
+ }
+
+ public UUID getStolenUnitId() {
+ return stolenUnitId;
+ }
+
+ public void setStolenUnitId(UUID id) {
+ stolenUnitId = id;
+ }
+
+ public static boolean isBreakingContract(Person person, LocalDate localDate, int ContractDuration) {
+ return ChronoUnit.MONTHS.between(person.getRecruitment(), localDate) < ContractDuration;
+ }
+ }
+
+ private String createCsv(Collection> coll) {
+ return StringUtils.join(coll, ",");
+ }
+
+ public void writeToXML(final PrintWriter pw, int indent) {
+ MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "retirementDefectionTracker");
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "rollRequired", createCsv(rollRequired));
+ MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "unresolvedPersonnel");
+ for (Integer i : unresolvedPersonnel.keySet()) {
+ MHQXMLUtility.writeSimpleXMLAttributedTag(pw, indent, "contract", "id", i, createCsv(unresolvedPersonnel.get(i)));
+ }
+ MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "unresolvedPersonnel");
+
+ MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "payouts");
+ for (UUID pid : payouts.keySet()) {
+ MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "payout", "id", pid);
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "weightClass", payouts.get(pid).getWeightClass());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "cbills", payouts.get(pid).getPayoutAmount());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "stolenUnit", payouts.get(pid).hasStolenUnit());
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "stolenUnitId", payouts.get(pid).getStolenUnitId());
+ MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "payout");
+ }
+ MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "payouts");
+
+ MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lastRetirementRoll", lastRetirementRoll);
+ MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "retirementDefectionTracker");
+ }
+
+ public static RetirementDefectionTracker generateInstanceFromXML(Node wn, Campaign c) {
+ RetirementDefectionTracker retVal = null;
+
+ try {
+ // Instantiate the correct child class, and call its parsing function.
+ retVal = new RetirementDefectionTracker();
+
+ // Okay, now load Part-specific fields!
+ NodeList nl = wn.getChildNodes();
+
+ // Loop through the nodes and load our contract offers
+ for (int x = 0; x < nl.getLength(); x++) {
+ Node wn2 = nl.item(x);
+
+ // If it's not an element node, we ignore it.
+ if (wn2.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+
+ if (wn2.getNodeName().equalsIgnoreCase("rollRequired")) {
+ if (!wn2.getTextContent().isBlank()) {
+ String [] ids = wn2.getTextContent().split(",");
+ for (String id : ids) {
+ retVal.rollRequired.add(Integer.parseInt(id));
+ }
+ }
+ } else if (wn2.getNodeName().equalsIgnoreCase("unresolvedPersonnel")) {
+ NodeList nl2 = wn2.getChildNodes();
+ for (int y = 0; y < nl2.getLength(); y++) {
+ Node wn3 = nl2.item(y);
+ if (wn3.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+ if (wn3.getNodeName().equalsIgnoreCase("contract")) {
+ int id = Integer.parseInt(wn3.getAttributes().getNamedItem("id").getTextContent());
+ String [] ids = wn3.getTextContent().split(",");
+ HashSet pids = Arrays
+ .stream(ids)
+ .map(UUID::fromString)
+ .collect(Collectors.toCollection(HashSet::new));
+ retVal.unresolvedPersonnel.put(id, pids);
+ }
+ }
+ } else if (wn2.getNodeName().equalsIgnoreCase("payouts")) {
+ NodeList nl2 = wn2.getChildNodes();
+ for (int y = 0; y < nl2.getLength(); y++) {
+ Node wn3 = nl2.item(y);
+ if (wn3.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+ if (wn3.getNodeName().equalsIgnoreCase("payout")) {
+ UUID pid = UUID.fromString(wn3.getAttributes().getNamedItem("id").getTextContent());
+ Payout payout = new Payout();
+ NodeList nl3 = wn3.getChildNodes();
+ for (int z = 0; z < nl3.getLength(); z++) {
+ Node wn4 = nl3.item(z);
+ if (wn4.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+ if (wn4.getNodeName().equalsIgnoreCase("weightClass")) {
+ payout.setWeightClass(Integer.parseInt(wn4.getTextContent()));
+ } else if (wn4.getNodeName().equalsIgnoreCase("c-bills")) {
+ payout.setPayoutAmount(Money.fromXmlString(wn4.getTextContent().trim()));
+ } else if (wn4.getNodeName().equalsIgnoreCase("stolenUnit")) {
+ payout.setStolenUnit(Boolean.parseBoolean(wn4.getTextContent()));
+ } else if (wn4.getNodeName().equalsIgnoreCase("stolenUnitId")) {
+ payout.setStolenUnitId(UUID.fromString(wn4.getTextContent()));
+ }
+ }
+ retVal.payouts.put(pid, payout);
+ }
+ }
+ } else if (wn2.getNodeName().equalsIgnoreCase("lastRetirementRoll")) {
+ retVal.setLastRetirementRoll(MHQXMLUtility.parseDate(wn2.getTextContent().trim()));
+ }
+ }
+ } catch (Exception ex) {
+ LogManager.getLogger().error("RetirementDefectionTracker: either the class name is invalid or the listed name doesn't exist.", ex);
+ }
+
+ if (retVal != null) {
+ // sometimes, a campaign may be loaded with orphan records in the Employee Turnover tracker
+ // let's clean those up here.
+ retVal.cleanupOrphans(c);
+ }
+
+ return retVal;
+ }
+}
diff --git a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java
index 214f025d6b..6c2d095b89 100644
--- a/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java
+++ b/MekHQ/src/mekhq/campaign/stratcon/StratconRulesManager.java
@@ -34,7 +34,9 @@
import mekhq.campaign.mission.ScenarioMapParameters.MapLocation;
import mekhq.campaign.mission.atb.AtBScenarioModifier;
import mekhq.campaign.mission.atb.AtBScenarioModifier.EventTiming;
+import mekhq.campaign.personnel.Person;
import mekhq.campaign.personnel.SkillType;
+import mekhq.campaign.personnel.turnoverAndRetention.Fatigue;
import mekhq.campaign.stratcon.StratconContractDefinition.StrategicObjectiveType;
import mekhq.campaign.stratcon.StratconScenario.ScenarioState;
import mekhq.campaign.unit.Unit;
@@ -43,6 +45,7 @@
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.util.*;
+import java.util.stream.Collectors;
/**
* This class contains "rules" logic for the AtB-Stratcon state
@@ -244,21 +247,17 @@ public static void setScenarioParametersFromBiome(StratconTrackState track, Stra
private static void swapInPlayerUnits(StratconScenario scenario, Campaign campaign, int explicitForceID) {
for (ScenarioForceTemplate sft : scenario.getScenarioTemplate().getAllScenarioForces()) {
if (sft.getGenerationMethod() == ForceGenerationMethod.PlayerOrFixedUnitCount.ordinal()) {
- int unitCount = 0;
+ int unitCount = (int) scenario.getBackingScenario().getBotUnitTemplates().values().stream()
+ .filter(template -> template.getForceName().equals(sft.getForceName()))
+ .count();
// get all the units that have been generated for this template
- for (ScenarioForceTemplate template : scenario.getBackingScenario().getBotUnitTemplates().values()) {
- if (template.getForceName().equals(sft.getForceName())) {
- unitCount++;
- }
- }
// or the units embedded in bot forces
- for (var tuple : scenario.getBackingScenario().getBotForceTemplates().entrySet()) {
- if (tuple.getValue().getForceName().equals(sft.getForceName())) {
- unitCount += tuple.getKey().getFullEntityList(campaign).size();
- }
- }
+ unitCount += scenario.getBackingScenario().getBotForceTemplates().entrySet().stream()
+ .filter(tuple -> tuple.getValue().getForceName().equals(sft.getForceName()))
+ .mapToInt(tuple -> tuple.getKey().getFullEntityList(campaign).size())
+ .sum();
// now we have a unit count. Don't bother with the next step if we don't have any substitutions to make
if (unitCount == 0) {
@@ -467,10 +466,19 @@ private static void processFacilityEffects(StratconTrackState track,
public static void processForceDeployment(StratconCoords coords, int forceID, Campaign campaign,
StratconTrackState track, boolean sticky) {
// plan of action:
+ // increase fatigue if the coordinates are not currently unrevealed
// reveal deployed coordinates
// reveal facility in deployed coordinates (and all adjacent coordinates for scout lances)
// reveal scenario in deployed coordinates (and all adjacent coordinates for scout lances)
+ // we want to ensure we only increase Fatigue once
+ boolean hasFatigueIncreased = false;
+
+ if (!track.getRevealedCoords().contains(coords)) {
+ increaseFatigue(forceID, campaign);
+ hasFatigueIncreased = true;
+ }
+
track.getRevealedCoords().add(coords);
StratconFacility facility = track.getFacility(coords);
@@ -505,6 +513,11 @@ public static void processForceDeployment(StratconCoords coords, int forceID, Ca
MekHQ.triggerEvent(new ScenarioChangedEvent(scenario.getBackingScenario()));
}
+ if ((!track.getRevealedCoords().contains(checkCoords)) && (!hasFatigueIncreased)) {
+ increaseFatigue(forceID, campaign);
+ hasFatigueIncreased = true;
+ }
+
track.getRevealedCoords().add(coords.translate(direction));
}
}
@@ -515,6 +528,21 @@ public static void processForceDeployment(StratconCoords coords, int forceID, Ca
MekHQ.triggerEvent(new StratconDeploymentEvent(campaign.getForce(forceID)));
}
+ /**
+ * Increases the fatigue for all crew members per Unit in a force.
+ *
+ * @param forceID the ID of the force
+ * @param campaign the campaign
+ */
+ private static void increaseFatigue(int forceID, Campaign campaign) {
+ for (UUID unit : campaign.getForce(forceID).getAllUnits(false)) {
+ for (Person person : campaign.getUnit(unit).getCrew()) {
+ person.increaseFatigue(campaign.getCampaignOptions().getFatigueRate());
+ Fatigue.processFatigueActions(campaign, person);
+ }
+ }
+ }
+
/**
* Worker function that processes the effects of deploying a reinforcement force to a scenario
*/
@@ -715,7 +743,7 @@ private static Map> sortForcesByMapType(List
* @return An informative string containing the reasons the user was nagged.
*/
public static String nagUnresolvedContacts(Campaign campaign) {
- StringBuilder sb = new StringBuilder();
+ String sb = "";
// check every track attached to an active contract for unresolved scenarios
// to which the player must deploy forces today
@@ -725,17 +753,16 @@ public static String nagUnresolvedContacts(Campaign campaign) {
}
for (StratconTrackState track : contract.getStratconCampaignState().getTracks()) {
- for (StratconScenario scenario : track.getScenarios().values()) {
- if ((scenario.getCurrentState() == ScenarioState.UNRESOLVED)
- && campaign.getLocalDate().equals(scenario.getDeploymentDate())) {
- // "scenario name, track name"
- sb.append(String.format("%s, %s\n", scenario.getName(), track.getDisplayableName()));
- }
- }
+ // "scenario name, track name"
+ sb = track.getScenarios().values().stream()
+ .filter(scenario -> (scenario.getCurrentState() == ScenarioState.UNRESOLVED)
+ && campaign.getLocalDate().equals(scenario.getDeploymentDate()))
+ .map(scenario -> String.format("%s, %s\n", scenario.getName(), track.getDisplayableName()))
+ .collect(Collectors.joining());
}
}
- return sb.toString();
+ return sb;
}
/**
@@ -1015,30 +1042,27 @@ public static boolean forceCompositionMatchesDeclaredUnitType(int primaryUnitTyp
* This is a set of all force IDs for forces that can be deployed to a scenario.
*
* @param campaign Current campaign
- * @return Set of available force IDs.
+ * @return List of available force IDs.
*/
public static List getAvailableForceIDs(Campaign campaign) {
- List retVal = new ArrayList<>();
// first, we gather a set of all forces that are already deployed to a track so
// we eliminate those later
- Set forcesInTracks = new HashSet<>();
- for (AtBContract contract : campaign.getActiveAtBContracts()) {
- for (StratconTrackState track : contract.getStratconCampaignState().getTracks()) {
- forcesInTracks.addAll(track.getAssignedForceCoords().keySet());
- }
- }
+ Set forcesInTracks = campaign.getActiveAtBContracts().stream()
+ .flatMap(contract -> contract.getStratconCampaignState().getTracks().stream())
+ .flatMap(track -> track.getAssignedForceCoords().keySet().stream())
+ .collect(Collectors.toSet());
// now, we get all the forces that qualify as "lances", and filter out those that are
// deployed to a scenario and not in a track already
- for (int key : campaign.getLances().keySet()) {
- Force force = campaign.getForce(key);
- if ((force != null) && !force.isDeployed() && !forcesInTracks.contains(force.getId())) {
- retVal.add(force.getId());
- }
- }
- return retVal;
+ return campaign.getLances().keySet().stream()
+ .mapToInt(key -> key)
+ .mapToObj(campaign::getForce).filter(force -> (force != null)
+ && !force.isDeployed()
+ && !forcesInTracks.contains(force.getId()))
+ .map(Force::getId)
+ .collect(Collectors.toList());
}
/**
@@ -1050,15 +1074,12 @@ public static List getAvailableForceIDs(int unitType, Campaign campaign
boolean reinforcements, @Nullable StratconScenario currentScenario, StratconCampaignState campaignState) {
List retVal = new ArrayList<>();
- Set forcesInTracks = new HashSet<>();
// assemble a set of all force IDs that are currently assigned to tracks that are not this one
- for (AtBContract contract : campaign.getActiveAtBContracts()) {
- for (StratconTrackState track : contract.getStratconCampaignState().getTracks()) {
- if ((track != currentTrack) || !reinforcements) {
- forcesInTracks.addAll(track.getAssignedForceCoords().keySet());
- }
- }
- }
+ Set forcesInTracks = campaign.getActiveAtBContracts().stream()
+ .flatMap(contract -> contract.getStratconCampaignState().getTracks().stream())
+ .filter(track -> (track != currentTrack) || !reinforcements)
+ .flatMap(track -> track.getAssignedForceCoords().keySet().stream())
+ .collect(Collectors.toSet());
// if there's an existing scenario and we're doing reinforcements,
// prevent forces that failed to deploy from trying to deploy again
@@ -1096,21 +1117,14 @@ private static boolean subElementsOrSelfDeployed(Force force, Campaign campaign)
return true;
}
- for (UUID unitID : force.getUnits()) {
- Unit unit = campaign.getUnit(unitID);
-
- if (unit.isDeployed()) {
- return true;
- }
- }
-
- for (Force child : force.getSubForces()) {
- if (subElementsOrSelfDeployed(child, campaign)) {
- return true;
- }
+ if (force.getUnits().stream()
+ .map(campaign::getUnit)
+ .anyMatch(Unit::isDeployed)) {
+ return true;
}
- return false;
+ return force.getSubForces().stream()
+ .anyMatch(child -> subElementsOrSelfDeployed(child, campaign));
}
/**
@@ -1275,12 +1289,10 @@ private static int getPrimaryUnitType(Campaign campaign, Set forceIDs)
public static ReinforcementEligibilityType getReinforcementType(int forceID, StratconTrackState trackState,
Campaign campaign, StratconCampaignState campaignState) {
// if the force is deployed elsewhere, it cannot be deployed as reinforcements
- for (AtBContract contract : campaign.getActiveAtBContracts()) {
- for (StratconTrackState track : contract.getStratconCampaignState().getTracks()) {
- if (track != trackState && track.getAssignedForceCoords().containsKey(forceID)) {
- return ReinforcementEligibilityType.None;
- }
- }
+ if (campaign.getActiveAtBContracts().stream()
+ .flatMap(contract -> contract.getStratconCampaignState().getTracks().stream())
+ .anyMatch(track -> !Objects.equals(track, trackState) && track.getAssignedForceCoords().containsKey(forceID))) {
+ return ReinforcementEligibilityType.None;
}
// TODO: If the force has completed a scenario which allows it,
@@ -1333,15 +1345,16 @@ public static boolean canManuallyDeployAnyForce(StratconCoords coords,
public static int calculateScenarioOdds(StratconTrackState track, AtBContract contract,
boolean playerDeployingForce) {
// rules:
- // rout morale: 0%
+ // broken morale: 0%
// very low morale: -10% when deploying forces to track, 0% attack
// low morale: -5%
// high morale: +5%
- // invincible: special case, let's do +10% for now
+ // very high morale: +10%
+ // unbreakable: special case, let's do +15% for now
int moraleModifier = 0;
switch (contract.getMoraleLevel()) {
- case ROUT:
+ case BROKEN:
return 0;
case VERY_LOW:
if (playerDeployingForce) {
@@ -1359,7 +1372,7 @@ public static int calculateScenarioOdds(StratconTrackState track, AtBContract co
case VERY_HIGH:
moraleModifier = 10;
break;
- case INVINCIBLE:
+ case UNBREAKABLE:
moraleModifier = 15;
break;
default:
@@ -1550,13 +1563,12 @@ public static void processTrackForceReturnDates(StratconTrackState track, Campai
* @return Whether or not we also need to get rid of the backing scenario from the campaign
*/
public static boolean processIgnoredScenario(AtBDynamicScenario scenario, StratconCampaignState campaignState) {
- for (StratconTrackState track : campaignState.getTracks()) {
- if (track.getBackingScenariosMap().containsKey(scenario.getId())) {
- return processIgnoredScenario(track.getBackingScenariosMap().get(scenario.getId()), campaignState);
- }
- }
+ return campaignState.getTracks().stream()
+ .filter(track -> track.getBackingScenariosMap().containsKey(scenario.getId()))
+ .findFirst()
+ .map(track -> processIgnoredScenario(track.getBackingScenariosMap().get(scenario.getId()), campaignState))
+ .orElse(true);
- return true;
}
/**
@@ -1689,13 +1701,10 @@ public void handleNewDay(NewDayEvent ev) {
* Worker function that goes through a track and cleans up scenarios missing required data
*/
private void cleanupPhantomScenarios(StratconTrackState track) {
- List cleanupList = new ArrayList<>();
-
- for (StratconScenario scenario : track.getScenarios().values()) {
- if ((scenario.getDeploymentDate() == null) && !scenario.isStrategicObjective()) {
- cleanupList.add(scenario);
- }
- }
+ List cleanupList = track.getScenarios().values().stream()
+ .filter(scenario -> (scenario.getDeploymentDate() == null)
+ && !scenario.isStrategicObjective())
+ .collect(Collectors.toList());
for (StratconScenario scenario : cleanupList) {
track.removeScenario(scenario);
diff --git a/MekHQ/src/mekhq/campaign/unit/Unit.java b/MekHQ/src/mekhq/campaign/unit/Unit.java
index bc74e5e4e2..767e4e7815 100644
--- a/MekHQ/src/mekhq/campaign/unit/Unit.java
+++ b/MekHQ/src/mekhq/campaign/unit/Unit.java
@@ -4732,6 +4732,30 @@ public void cancelMothballOrActivation() {
}
//endregion Mothballing/Activation
+ /**
+ * Returns a list of all soldiers in a unit.
+ *
+ * @return a list of Person objects representing the soldiers.
+ */
+ public List getSoldiers() {
+ // soldiers appear in both the drivers and gunners list,
+ // so we use drivers
+ return drivers.stream()
+ .filter(person -> entity instanceof Infantry)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Retrieves a list of uninjured soldiers in a unit.
+ *
+ * @return The list of uninjured soldiers.
+ */
+ public List getActiveSoldiers() {
+ return getSoldiers().stream()
+ .filter(person -> person.getHits() == 0)
+ .collect(Collectors.toList());
+ }
+
public List getActiveCrew() {
List crew = new ArrayList<>();
for (Person p : drivers) {
diff --git a/MekHQ/src/mekhq/gui/BriefingTab.java b/MekHQ/src/mekhq/gui/BriefingTab.java
index a17e39fdef..0b51d2dd0d 100644
--- a/MekHQ/src/mekhq/gui/BriefingTab.java
+++ b/MekHQ/src/mekhq/gui/BriefingTab.java
@@ -357,36 +357,36 @@ private void completeMission() {
if (((AtBContract) mission).contractExtended(getCampaign())) {
return;
}
+ }
- if (getCampaign().getCampaignOptions().getRandomRetirementMethod().isAgainstTheBot()
- && getCampaign().getCampaignOptions().isUseContractCompletionRandomRetirement()) {
- RetirementDefectionDialog rdd = new RetirementDefectionDialog(getCampaignGui(),
- (AtBContract) mission, true);
- rdd.setVisible(true);
- if (rdd.wasAborted()) {
- /*
- * Once the retirement rolls have been made, the outstanding payouts can be resolved
- * without reference to the contract and the dialog can be accessed through the menu
- * provided they aren't still assigned to the mission in question.
- */
- if (!getCampaign().getRetirementDefectionTracker().isOutstanding(mission.getId())) {
- return;
- }
- } else {
- if ((getCampaign().getRetirementDefectionTracker().getRetirees(mission) != null)
- && getCampaign().getFinances().getBalance().isGreaterOrEqualThan(rdd.totalPayout())) {
- for (PersonnelRole role : PersonnelRole.getAdministratorRoles()) {
- Person admin = getCampaign().findBestInRole(role, SkillType.S_ADMIN);
- if (admin != null) {
- admin.awardXP(getCampaign(), 1);
- getCampaign().addReport(admin.getHyperlinkedName() + " has gained 1 XP.");
- }
+ if (getCampaign().getCampaignOptions().isUseRandomRetirement()
+ && getCampaign().getCampaignOptions().isUseContractCompletionRandomRetirement()) {
+ RetirementDefectionDialog rdd = new RetirementDefectionDialog(getCampaignGui(), mission, true);
+ rdd.setVisible(true);
+
+ if (rdd.wasAborted()) {
+ /*
+ * Once the retirement rolls have been made, the outstanding payouts can be resolved
+ * without a reference to the contract and the dialog can be accessed through the menu
+ * provided they aren't still assigned to the mission in question.
+ */
+ if (!getCampaign().getRetirementDefectionTracker().isOutstanding(mission.getId())) {
+ return;
+ }
+ } else {
+ if ((getCampaign().getRetirementDefectionTracker().getRetirees(mission) != null)
+ && getCampaign().getFinances().getBalance().isGreaterOrEqualThan(rdd.totalPayout())) {
+ for (PersonnelRole role : PersonnelRole.getAdministratorRoles()) {
+ Person admin = getCampaign().findBestInRole(role, SkillType.S_ADMIN);
+ if (admin != null) {
+ admin.awardXP(getCampaign(), 1);
+ getCampaign().addReport(admin.getHyperlinkedName() + " has gained 1 XP.");
}
}
+ }
- if (!getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments())) {
- return;
- }
+ if (!getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments())) {
+ return;
}
}
}
@@ -487,12 +487,13 @@ private void resolveScenario() {
// tracker.postProcessEntities(control);
ResolveScenarioWizardDialog resolveDialog = new ResolveScenarioWizardDialog(getFrame(), true, tracker);
resolveDialog.setVisible(true);
- if (getCampaign().getCampaignOptions().isUseAtB()
- && getCampaign().getMission(scenario.getMissionId()) instanceof AtBContract
- && !getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty()) {
+
+ if (!getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty()) {
RetirementDefectionDialog rdd = new RetirementDefectionDialog(getCampaignGui(),
- (AtBContract) getCampaign().getMission(scenario.getMissionId()), false);
+ getCampaign().getMission(scenario.getMissionId()), false);
+
rdd.setVisible(true);
+
if (!rdd.wasAborted()) {
getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments());
}
diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java
index ac62cb4204..a65ae5eac9 100644
--- a/MekHQ/src/mekhq/gui/CampaignGUI.java
+++ b/MekHQ/src/mekhq/gui/CampaignGUI.java
@@ -925,7 +925,7 @@ private void initMenu() {
miRetirementDefectionDialog = new JMenuItem(resourceMap.getString("miRetirementDefectionDialog.text"));
miRetirementDefectionDialog.setMnemonic(KeyEvent.VK_R);
miRetirementDefectionDialog.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.ALT_DOWN_MASK));
- miRetirementDefectionDialog.setVisible(!getCampaign().getCampaignOptions().getRandomRetirementMethod().isNone());
+ miRetirementDefectionDialog.setVisible(getCampaign().getCampaignOptions().isUseRandomRetirement());
miRetirementDefectionDialog.addActionListener(evt -> showRetirementDefectionDialog());
menuView.add(miRetirementDefectionDialog);
@@ -1210,6 +1210,7 @@ public void showRetirementDefectionDialog() {
RetirementDefectionDialog rdd = new RetirementDefectionDialog(this, null,
getCampaign().getRetirementDefectionTracker().getRetirees().isEmpty());
rdd.setVisible(true);
+
if (!rdd.wasAborted()) {
getCampaign().applyRetirement(rdd.totalPayout(), rdd.getUnitAssignments());
}
@@ -1573,6 +1574,8 @@ private void menuOptionsActionPerformed(final ActionEvent evt) {
}
}
+ getCampaign().initTurnover();
+
if (staticRATs != newOptions.isUseStaticRATs()) {
getCampaign().initUnitGenerator();
}
@@ -2397,10 +2400,12 @@ public void handleDayEnding(DayEndingEvent evt) {
return;
}
- if (getCampaign().checkYearlyRetirements()) {
- showRetirementDefectionDialog();
- evt.cancel();
- return;
+ if (getCampaign().getCampaignOptions().isUseRandomRetirement()) {
+ if (getCampaign().checkTurnoverPrompt()) {
+ showRetirementDefectionDialog();
+ evt.cancel();
+ return;
+ }
}
if(getCampaign().checkScenariosDue()) {
@@ -2490,7 +2495,7 @@ public void handle(final OptionsChangedEvent evt) {
fundsScheduler.schedule();
refreshPartsAvailability();
- miRetirementDefectionDialog.setVisible(!evt.getOptions().getRandomRetirementMethod().isNone());
+ miRetirementDefectionDialog.setVisible(evt.getOptions().isUseRandomRetirement());
miAwardEligibilityDialog.setVisible((evt.getOptions().isEnableAutoAwards()));
miUnitMarket.setVisible(!evt.getOptions().getUnitMarketMethod().isNone());
}
diff --git a/MekHQ/src/mekhq/gui/CommandCenterTab.java b/MekHQ/src/mekhq/gui/CommandCenterTab.java
index 7e97bf2cef..4f98d7448d 100644
--- a/MekHQ/src/mekhq/gui/CommandCenterTab.java
+++ b/MekHQ/src/mekhq/gui/CommandCenterTab.java
@@ -22,8 +22,8 @@
import megamek.client.ui.swing.dialog.AbstractUnitSelectorDialog;
import megamek.common.MechSummaryCache;
import megamek.common.event.Subscribe;
-import mekhq.MekHQ;
import mekhq.MHQOptionsChangedEvent;
+import mekhq.MekHQ;
import mekhq.campaign.event.*;
import mekhq.campaign.report.CargoReport;
import mekhq.campaign.report.HangarReport;
@@ -46,7 +46,6 @@
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
-import java.util.List;
import java.util.ResourceBundle;
/**
@@ -61,11 +60,14 @@ public final class CommandCenterTab extends CampaignGuiTab {
private JLabel lblRating;
private JLabel lblExperience;
private JLabel lblPersonnel;
+ private JLabel lblMorale;
+ private JLabel lblAdminstrativeCapacity;
private JLabel lblMissionSuccess;
private JLabel lblComposition;
private JLabel lblRepairStatus;
private JLabel lblTransportCapacity;
private JLabel lblCargoSummary;
+ private JLabel lblFacilityCapacities;
// objectives panel
private JPanel panObjectives;
@@ -202,6 +204,7 @@ private void initInfoPanel() {
gridBagConstraints.gridx = 1;
gridBagConstraints.weightx = 1.0;
panInfo.add(lblRating, gridBagConstraints);
+
JLabel lblExperienceHead = new JLabel(resourceMap.getString("lblExperience.text"));
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
@@ -244,6 +247,22 @@ private void initInfoPanel() {
gridBagConstraints.weightx = 1.0;
panInfo.add(lblPersonnel, gridBagConstraints);
+ if (getCampaign().getCampaignOptions().isUseAdministrativeStrain()) {
+ JLabel lblAdministrativeCapacityHead = new JLabel(resourceMap.getString("lblAdministrativeCapacity.text"));
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = y++;
+ gridBagConstraints.fill = GridBagConstraints.NONE;
+ gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+ gridBagConstraints.insets = new Insets(1, 5, 1, 5);
+ panInfo.add(lblAdministrativeCapacityHead, gridBagConstraints);
+ lblAdminstrativeCapacity = new JLabel(getCampaign().getCampaignSummary().getAdministrativeCapacityReport(getCampaign()));
+ lblAdministrativeCapacityHead.setLabelFor(lblAdminstrativeCapacity);
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.weightx = 1.0;
+ panInfo.add(lblAdminstrativeCapacity, gridBagConstraints);
+ }
+
JLabel lblCompositionHead = new JLabel(resourceMap.getString("lblComposition.text"));
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
@@ -292,7 +311,7 @@ private void initInfoPanel() {
gridBagConstraints.gridy = y++;
gridBagConstraints.fill = GridBagConstraints.NONE;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
- gridBagConstraints.insets = new Insets(1, 5, 5, 5);
+ gridBagConstraints.insets = new Insets(1, 5, 1, 5);
panInfo.add(lblCargoSummaryHead, gridBagConstraints);
lblCargoSummary = new JLabel(getCampaign().getCampaignSummary().getCargoCapacityReport());
lblCargoSummaryHead.setLabelFor(lblCargoSummary);
@@ -300,6 +319,22 @@ private void initInfoPanel() {
gridBagConstraints.weightx = 1.0;
panInfo.add(lblCargoSummary, gridBagConstraints);
+ if (getCampaign().getCampaignOptions().isUseFatigue()) {
+ JLabel lblFacilityCapacitiesHead = new JLabel(resourceMap.getString("lblFacilityCapacities.text"));
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = y++;
+ gridBagConstraints.fill = GridBagConstraints.NONE;
+ gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+ gridBagConstraints.insets = new Insets(1, 5, 1, 5);
+ panInfo.add(lblFacilityCapacitiesHead, gridBagConstraints);
+ lblFacilityCapacities = new JLabel(getCampaign().getCampaignSummary().getFatigueSummary());
+ lblFacilityCapacitiesHead.setLabelFor(lblFacilityCapacities);
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.weightx = 1.0;
+ panInfo.add(lblFacilityCapacities, gridBagConstraints);
+ }
+
panInfo.setBorder(BorderFactory.createTitledBorder(resourceMap.getString("panInfo.title")));
}
@@ -514,6 +549,18 @@ private void refreshBasicInfo() {
lblCargoSummary.setText(getCampaign().getCampaignSummary().getCargoCapacityReport());
lblRepairStatus.setText(getCampaign().getCampaignSummary().getForceRepairReport());
lblTransportCapacity.setText(getCampaign().getCampaignSummary().getTransportCapacity());
+
+ if (getCampaign().getCampaignOptions().isUseAdministrativeStrain()) {
+ try {
+ lblAdminstrativeCapacity.setText(getCampaign().getCampaignSummary().getAdministrativeCapacityReport(getCampaign()));
+ } catch (Exception ignored) {}
+ }
+
+ if (getCampaign().getCampaignOptions().isUseFatigue()) {
+ try {
+ lblFacilityCapacities.setText(getCampaign().getCampaignSummary().getFatigueSummary());
+ } catch (Exception ignored) {}
+ }
}
private void refreshObjectives() {
diff --git a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java
index 56aa46509f..0e60bfec73 100644
--- a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java
+++ b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java
@@ -21,6 +21,7 @@
import megamek.client.generator.RandomCallsignGenerator;
import megamek.client.generator.RandomNameGenerator;
import megamek.client.ui.dialogs.PortraitChooserDialog;
+import megamek.common.Compute;
import megamek.common.Crew;
import megamek.common.Mounted;
import megamek.common.options.IOption;
@@ -127,6 +128,7 @@ public class PersonnelTableMouseAdapter extends JPopupMenuAdapter {
private static final String CMD_REMOVE_SPOUSE = "REMOVE_SPOUSE";
private static final String CMD_ADD_PREGNANCY = "ADD_PREGNANCY";
private static final String CMD_REMOVE_PREGNANCY = "PREGNANCY_SPOUSE";
+ private static final String CMD_LOYALTY = "LOYALTY";
private static final String CMD_IMPRISON = "IMPRISON";
private static final String CMD_FREE = "FREE";
@@ -969,6 +971,13 @@ public void actionPerformed(ActionEvent action) {
break;
}
+ case CMD_LOYALTY: {
+ for (Person person : people) {
+ person.generateLoyalty(Compute.d6(3));
+ MekHQ.triggerEvent(new PersonChangedEvent(person));
+ }
+ break;
+ }
//region Randomization Menu
case CMD_RANDOM_NAME: {
@@ -1076,8 +1085,7 @@ protected Optional createPopupMenu() {
if (StaticChecks.areAllEligible(true, selected)) {
menu = new JMenu(resources.getString("changeRank.text"));
final Profession initialProfession = Profession.getProfessionFromPersonnelRole(person.getPrimaryRole());
- for (final RankDisplay rankDisplay : RankDisplay.getRankDisplaysForSystem(
- person.getRankSystem(), initialProfession)) {
+ for (final RankDisplay rankDisplay : RankDisplay.getRankDisplaysForSystem(person.getRankSystem(), initialProfession)) {
final Rank rank = person.getRankSystem().getRank(rankDisplay.getRankNumeric());
final Profession profession = initialProfession.getProfession(person.getRankSystem(), rank);
final int rankLevels = rank.getRankLevels().get(profession);
@@ -1085,12 +1093,9 @@ protected Optional createPopupMenu() {
if (rankLevels > 1) {
submenu = new JMenu(rankDisplay.toString());
for (int level = 0; level <= rankLevels; level++) {
- cbMenuItem = new JCheckBoxMenuItem(rank.getName(profession)
- + Utilities.getRomanNumeralsFromArabicNumber(level, true));
- cbMenuItem.setSelected((person.getRankNumeric() == rankDisplay.getRankNumeric())
- && (person.getRankLevel() == level));
- cbMenuItem.setActionCommand(makeCommand(CMD_RANK,
- String.valueOf(rankDisplay.getRankNumeric()), String.valueOf(level)));
+ cbMenuItem = new JCheckBoxMenuItem(rank.getName(profession) + Utilities.getRomanNumeralsFromArabicNumber(level, true));
+ cbMenuItem.setSelected((person.getRankNumeric() == rankDisplay.getRankNumeric()) && (person.getRankLevel() == level));
+ cbMenuItem.setActionCommand(makeCommand(CMD_RANK, String.valueOf(rankDisplay.getRankNumeric()), String.valueOf(level)));
cbMenuItem.addActionListener(this);
submenu.add(cbMenuItem);
}
@@ -1202,8 +1207,7 @@ protected Optional createPopupMenu() {
popup.add(newMenuItem(resources.getString("free.text"), CMD_FREE));
}
- if (gui.getCampaign().getCampaignOptions().isUseAtBPrisonerRansom()
- && StaticChecks.areAllPrisoners(selected)) {
+ if (gui.getCampaign().getCampaignOptions().isUseAtBPrisonerRansom() && StaticChecks.areAllPrisoners(selected)) {
popup.add(newMenuItem(resources.getString("ransom.text"), CMD_RANSOM));
}
@@ -1277,24 +1281,17 @@ protected Optional createPopupMenu() {
final String status;
final String founder = potentialSpouse.isFounder() ? resources.getString("spouseFounder.text") : "";
if (potentialSpouse.getPrisonerStatus().isBondsman()) {
- status = String.format(resources.getString("marriageBondsmanDesc.format"),
- potentialSpouse.getFullName(), potentialSpouse.getAge(today),
- potentialSpouse.getRoleDesc(), founder);
+ status = String.format(resources.getString("marriageBondsmanDesc.format"), potentialSpouse.getFullName(), potentialSpouse.getAge(today), potentialSpouse.getRoleDesc(), founder);
} else if (potentialSpouse.getPrisonerStatus().isCurrentPrisoner()) {
- status = String.format(resources.getString("marriagePrisonerDesc.format"),
- potentialSpouse.getFullName(), potentialSpouse.getAge(today),
- potentialSpouse.getRoleDesc(), founder);
+ status = String.format(resources.getString("marriagePrisonerDesc.format"), potentialSpouse.getFullName(), potentialSpouse.getAge(today), potentialSpouse.getRoleDesc(), founder);
} else {
- status = String.format(resources.getString("marriagePartnerDesc.format"),
- potentialSpouse.getFullName(), potentialSpouse.getAge(today),
- potentialSpouse.getRoleDesc(), founder);
+ status = String.format(resources.getString("marriagePartnerDesc.format"), potentialSpouse.getFullName(), potentialSpouse.getAge(today), potentialSpouse.getRoleDesc(), founder);
}
spouseMenu = new JMenu(status);
for (final MergingSurnameStyle style : MergingSurnameStyle.values()) {
- spouseMenu.add(newMenuItem(style.getDropDownText(),
- makeCommand(CMD_ADD_SPOUSE, potentialSpouse.getId().toString(), style.name())));
+ spouseMenu.add(newMenuItem(style.getDropDownText(), makeCommand(CMD_ADD_SPOUSE, potentialSpouse.getId().toString(), style.name())));
}
if (potentialSpouse.getGender().isMale()) {
@@ -1316,8 +1313,7 @@ protected Optional createPopupMenu() {
}
}
- if (gui.getCampaign().getCampaignOptions().isUseManualDivorce()
- && Stream.of(selected).anyMatch(p -> gui.getCampaign().getDivorce().canDivorce(person, false) == null)) {
+ if (gui.getCampaign().getCampaignOptions().isUseManualDivorce() && Stream.of(selected).anyMatch(p -> gui.getCampaign().getDivorce().canDivorce(person, false) == null)) {
menu = new JMenu(resources.getString("removeSpouse.text"));
for (final SplittingSurnameStyle style : SplittingSurnameStyle.values()) {
@@ -2522,6 +2518,13 @@ protected Optional createPopupMenu() {
}
}
+ if (gui.getCampaign().getCampaignOptions().isUseLoyaltyModifiers()) {
+ menuItem = new JMenuItem(resources.getString("regenerateLoyalty.text"));
+ menuItem.setActionCommand(CMD_LOYALTY);
+ menuItem.addActionListener(this);
+ menu.add(menuItem);
+ }
+
JMenuHelpers.addMenuIfNonEmpty(popup, menu);
}
//endregion GM Menu
diff --git a/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java b/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java
index 01bcf73ec5..ba1adb74a9 100644
--- a/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java
@@ -26,6 +26,7 @@
import megamek.client.ui.swing.DialogOptionListener;
import megamek.common.Crew;
import megamek.common.EquipmentType;
+import megamek.common.TechConstants;
import megamek.common.enums.Gender;
import megamek.common.options.IOption;
import megamek.common.options.IOptionGroup;
@@ -44,8 +45,8 @@
import mekhq.gui.baseComponents.AbstractMHQScrollablePanel;
import mekhq.gui.baseComponents.DefaultMHQScrollablePanel;
import mekhq.gui.control.EditKillLogControl;
-import mekhq.gui.control.EditScenarioLogControl;
import mekhq.gui.control.EditPersonnelLogControl;
+import mekhq.gui.control.EditScenarioLogControl;
import mekhq.gui.utilities.MarkdownEditorPanel;
import org.apache.logging.log4j.LogManager;
@@ -86,6 +87,7 @@ public class CustomizePersonDialog extends JDialog implements DialogOptionListen
private AbstractMHQScrollablePanel skillsPanel;
private AbstractMHQScrollablePanel optionsPanel;
private JTextField textToughness;
+ private JTextField textFatigue;
private JTextField textPreNominal;
private JTextField textGivenName;
private JTextField textSurname;
@@ -160,7 +162,9 @@ private void initComponents() {
textNickname = new JTextField();
textBloodname = new JTextField();
textToughness = new JTextField();
+ textFatigue = new JTextField();
JLabel lblToughness = new JLabel();
+ JLabel lblFatigue = new JLabel();
JScrollPane scrOptions = new JScrollPane();
JScrollPane scrSkills = new JScrollPane();
JPanel panButtons = new JPanel();
@@ -637,6 +641,31 @@ public Component getListCellRendererComponent(final JList> list,
gridBagConstraints.anchor = GridBagConstraints.WEST;
gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
panDemog.add(textToughness, gridBagConstraints);
+
+ y++;
+ }
+
+ lblFatigue.setText(resourceMap.getString("lblFatigue.text"));
+ lblFatigue.setName("lblFatigue");
+
+ textFatigue.setText(Integer.toString(person.getFatigue()));
+ textFatigue.setName("textFatigue");
+
+ if (campaign.getCampaignOptions().isUseFatigue()) {
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = y;
+ gridBagConstraints.anchor = GridBagConstraints.WEST;
+ gridBagConstraints.insets = new Insets(0, 5, 0, 0);
+ panDemog.add(lblFatigue, gridBagConstraints);
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.gridy = y;
+ gridBagConstraints.anchor = GridBagConstraints.WEST;
+ gridBagConstraints.fill = GridBagConstraints.HORIZONTAL;
+ panDemog.add(textFatigue, gridBagConstraints);
+
+ y++;
}
JLabel lblUnit = new JLabel();
@@ -686,19 +715,19 @@ public Component getListCellRendererComponent(JList> list,
choiceOriginalUnit.setSelectedItem(campaign.getUnit(person.getOriginalUnitId()));
}
choiceOriginalUnit.addActionListener(ev -> {
- if (null == choiceOriginalUnit.getSelectedItem()) {
- choiceUnitWeight.setSelectedIndex(0);
- choiceUnitTech.setSelectedIndex(0);
- } else {
+ try {
Unit unit = (Unit) choiceOriginalUnit.getSelectedItem();
choiceUnitWeight.setSelectedIndex(unit.getEntity().getWeightClass());
if (unit.getEntity().isClan()) {
choiceUnitTech.setSelectedIndex(2);
- } else if (unit.getEntity().getTechLevel() > megamek.common.TechConstants.T_INTRO_BOXSET) {
+ } else if (unit.getEntity().getTechLevel() > TechConstants.T_INTRO_BOXSET) {
choiceUnitTech.setSelectedIndex(1);
} else {
choiceUnitTech.setSelectedIndex(0);
}
+ } catch (Exception e) {
+ choiceUnitWeight.setSelectedIndex(person.getOriginalUnitWeight());
+ choiceUnitTech.setSelectedIndex(person.getOriginalUnitTech());
}
});
@@ -967,7 +996,11 @@ private void btnOkActionPerformed(ActionEvent evt) {
try {
person.setToughness(Integer.parseInt(textToughness.getText()));
} catch (NumberFormatException ignored) { }
- if (null == choiceOriginalUnit.getSelectedItem()) {
+ try {
+ person.setFatigue(Integer.parseInt(textFatigue.getText()));
+ } catch (NumberFormatException ignored) { }
+ if (choiceOriginalUnit.getSelectedItem() == null) {
+ person.setOriginalUnit(null);
person.setOriginalUnitWeight(choiceUnitWeight.getSelectedIndex());
person.setOriginalUnitTech(choiceUnitTech.getSelectedIndex());
} else {
diff --git a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java
index 502866e2ee..68c745349e 100644
--- a/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/DataLoadingDialog.java
@@ -336,6 +336,9 @@ public Campaign doInBackground() throws Exception {
if (campaign.getCampaignOptions().isUseAtB()) {
campaign.initAtB(true);
}
+
+ // Turnover
+ campaign.initTurnover();
//endregion Progress 7
} else {
//region Progress 6
diff --git a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java
index bca9b91e87..6d577ddf1b 100644
--- a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java
@@ -67,6 +67,9 @@ public class GMToolsDialog extends AbstractMHQDialog {
private JTabbedPane tabbedPane;
//region General Tab
+ // General Tools Panel
+ private JSpinner spnMorale;
+
// Dice Panel
private JSpinner spnDiceCount;
private JSpinner spnDiceNumber;
@@ -185,6 +188,14 @@ public void setSpnDiceSides(final JSpinner spnDiceSides) {
this.spnDiceSides = spnDiceSides;
}
+ public JSpinner getSpnMorale() {
+ return spnMorale;
+ }
+
+ public void setSpnMorale(final JSpinner spnMorale) {
+ this.spnMorale = spnMorale;
+ }
+
public JLabel getLblTotalDiceResult() {
return lblTotalDiceResult;
}
diff --git a/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java b/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java
index 703a2eb350..6ec43a76af 100644
--- a/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/MHQOptionsDialog.java
@@ -1144,8 +1144,6 @@ protected void okAction() {
MekHQ.getMHQOptions().setHealedInjuriesBackground(optionHealedInjuriesBackground.getColour());
MekHQ.getMHQOptions().setPregnantForeground(optionPregnantForeground.getColour());
MekHQ.getMHQOptions().setPregnantBackground(optionPregnantBackground.getColour());
- MekHQ.getMHQOptions().setPaidRetirementForeground(optionPaidRetirementForeground.getColour());
- MekHQ.getMHQOptions().setPaidRetirementBackground(optionPaidRetirementBackground.getColour());
MekHQ.getMHQOptions().setStratConHexCoordForeground(optionStratConHexCoordForeground.getColour());
MekHQ.getMHQOptions().setMedicalViewDialogHandwritingFont(comboMedicalViewDialogHandwritingFont.getFont().getFamily());
@@ -1253,8 +1251,6 @@ private void setInitialState() {
optionHealedInjuriesBackground.setColour(MekHQ.getMHQOptions().getHealedInjuriesBackground());
optionPregnantForeground.setColour(MekHQ.getMHQOptions().getPregnantForeground());
optionPregnantBackground.setColour(MekHQ.getMHQOptions().getPregnantBackground());
- optionPaidRetirementForeground.setColour(MekHQ.getMHQOptions().getPaidRetirementForeground());
- optionPaidRetirementBackground.setColour(MekHQ.getMHQOptions().getPaidRetirementBackground());
optionStratConHexCoordForeground.setColour(MekHQ.getMHQOptions().getStratConHexCoordForeground());
comboMedicalViewDialogHandwritingFont.setSelectedItem(new FontDisplay(MekHQ.getMHQOptions().getMedicalViewDialogHandwritingFont()));
diff --git a/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java b/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java
index 55392cb849..7d92f575d9 100644
--- a/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java
+++ b/MekHQ/src/mekhq/gui/dialog/RetirementDefectionDialog.java
@@ -23,17 +23,16 @@
import megamek.client.ui.preferences.JIntNumberSpinnerPreference;
import megamek.client.ui.preferences.JWindowPreference;
import megamek.client.ui.preferences.PreferencesNode;
-import megamek.common.Compute;
import megamek.common.Entity;
import megamek.common.TargetRoll;
+import megamek.common.TechConstants;
import megamek.common.UnitType;
import mekhq.MekHQ;
import mekhq.campaign.finances.Money;
import mekhq.campaign.finances.enums.TransactionType;
-import mekhq.campaign.mission.AtBContract;
+import mekhq.campaign.mission.Mission;
import mekhq.campaign.personnel.Person;
-import mekhq.campaign.personnel.RetirementDefectionTracker;
-import mekhq.campaign.personnel.enums.PersonnelRole;
+import mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker;
import mekhq.campaign.unit.Unit;
import mekhq.gui.CampaignGUI;
import mekhq.gui.enums.PersonnelFilter;
@@ -45,6 +44,8 @@
import org.apache.logging.log4j.LogManager;
import javax.swing.*;
+import javax.swing.JSpinner.DefaultEditor;
+import javax.swing.RowSorter.SortKey;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;
@@ -55,6 +56,8 @@
import java.util.List;
import java.util.*;
+import static mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker.Payout.isBreakingContract;
+
/**
* @author Neoancient
*/
@@ -64,12 +67,12 @@ public class RetirementDefectionDialog extends JDialog {
private String currentPanel;
- private CampaignGUI hqView;
- private AtBContract contract;
- private RetirementDefectionTracker rdTracker;
+ final private CampaignGUI hqView;
+ final private Mission contract;
+ final private RetirementDefectionTracker rdTracker;
private Map targetRolls;
- private Map unitAssignments;
+ final private Map unitAssignments;
private JPanel panMain;
private JTextArea txtInstructions;
@@ -106,14 +109,14 @@ public class RetirementDefectionDialog extends JDialog {
private final ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.RetirementDefectionDialog",
MekHQ.getMHQOptions().getLocale());
- public RetirementDefectionDialog (CampaignGUI gui, AtBContract contract, boolean doRetirement) {
+ public RetirementDefectionDialog (CampaignGUI gui, Mission mission, boolean doRetirement) {
super(gui.getFrame(), true);
hqView = gui;
unitAssignments = new HashMap<>();
- this.contract = contract;
+ this.contract = mission;
rdTracker = hqView.getCampaign().getRetirementDefectionTracker();
if (doRetirement) {
- targetRolls = rdTracker.calculateTargetNumbers(contract, hqView.getCampaign());
+ targetRolls = rdTracker.getTargetNumbers(mission, hqView.getCampaign());
}
currentPanel = doRetirement?PAN_OVERVIEW:PAN_RESULTS;
setSize(new Dimension(800, 600));
@@ -130,6 +133,9 @@ public RetirementDefectionDialog (CampaignGUI gui, AtBContract contract, boolean
private void initComponents(boolean doRetirement) {
setTitle(resourceMap.getString("title.text"));
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ setSize(screenSize);
+
setLayout(new BorderLayout());
cardLayout = new CardLayout();
panMain = new JPanel(cardLayout);
@@ -167,6 +173,7 @@ private void initComponents(boolean doRetirement) {
for (PersonnelFilter filter : MekHQ.getMHQOptions().getPersonnelFilterStyle().getFilters(true)) {
cbGroupOverview.addItem(filter);
}
+
JPanel panTop = new JPanel();
panTop.setLayout(new BoxLayout(panTop, BoxLayout.X_AXIS));
panTop.add(cbGroupOverview);
@@ -204,8 +211,8 @@ private void initComponents(boolean doRetirement) {
personnelSorter.setComparator(RetirementTableModel.COL_PAYOUT, new FormattedNumberSorter());
personnelSorter.setComparator(RetirementTableModel.COL_BONUS_COST, new FormattedNumberSorter());
personnelTable.setRowSorter(personnelSorter);
- ArrayList sortKeys = new ArrayList<>();
- sortKeys.add(new RowSorter.SortKey(RetirementTableModel.COL_PERSON, SortOrder.DESCENDING));
+ ArrayList sortKeys = new ArrayList<>();
+ sortKeys.add(new SortKey(RetirementTableModel.COL_PERSON, SortOrder.DESCENDING));
personnelSorter.setSortKeys(sortKeys);
cbGroupOverview.addActionListener(evt -> filterPersonnel(personnelSorter, cbGroupOverview, false));
@@ -217,7 +224,7 @@ private void initComponents(boolean doRetirement) {
int row = personnelTable.convertRowIndexToModel(personnelTable.getSelectedRow());
UUID id = ((RetirementTableModel)(personnelTable.getModel())).getPerson(row).getId();
txtTargetDetails.setText(targetRolls.get(id).getDesc() +
- (payBonus(id)?" -2 (Bonus)":"") +
+ (payBonus(id)?" -2 (Bonus)":" ") +
((miscModifier(id) != 0)?miscModifier(id) + " (Misc)":""));
});
@@ -226,7 +233,6 @@ private void initComponents(boolean doRetirement) {
XTableColumnModel columnModel = (XTableColumnModel) personnelTable.getColumnModel();
columnModel.setColumnVisible(columnModel.getColumn(personnelTable.convertColumnIndexToView(RetirementTableModel.COL_PAYOUT)), false);
columnModel.setColumnVisible(columnModel.getColumn(personnelTable.convertColumnIndexToView(RetirementTableModel.COL_UNIT)), false);
- columnModel.setColumnVisible(columnModel.getColumn(personnelTable.convertColumnIndexToView(RetirementTableModel.COL_RECRUIT)), false);
if (hqView.getCampaign().getCampaignOptions().isUseShareSystem()) {
columnModel.setColumnVisible(columnModel.getColumn(personnelTable.convertColumnIndexToView(RetirementTableModel.COL_BONUS_COST)), false);
columnModel.setColumnVisible(columnModel.getColumn(personnelTable.convertColumnIndexToView(RetirementTableModel.COL_PAY_BONUS)), false);
@@ -307,8 +313,8 @@ private void initComponents(boolean doRetirement) {
retireeSorter = new TableRowSorter<>(model);
retireeSorter.setComparator(RetirementTableModel.COL_PERSON, new PersonRankStringSorter(hqView.getCampaign()));
retireeTable.setRowSorter(retireeSorter);
- ArrayList sortKeys = new ArrayList<>();
- sortKeys.add(new RowSorter.SortKey(RetirementTableModel.COL_PERSON, SortOrder.DESCENDING));
+ ArrayList sortKeys = new ArrayList<>();
+ sortKeys.add(new SortKey(RetirementTableModel.COL_PERSON, SortOrder.DESCENDING));
retireeSorter.setSortKeys(sortKeys);
cbGroupResults.addActionListener(evt -> filterPersonnel(retireeSorter, cbGroupResults, true));
@@ -337,9 +343,9 @@ private void initComponents(boolean doRetirement) {
unitSorter = new TableRowSorter<>(unitModel);
unitSorter.setComparator(UnitAssignmentTableModel.COL_UNIT, new WeightClassSorter());
unitAssignmentTable.setRowSorter(unitSorter);
- ArrayList unitSortKeys = new ArrayList<>();
- unitSortKeys.add(new RowSorter.SortKey(UnitAssignmentTableModel.COL_UNIT, SortOrder.DESCENDING));
- sortKeys.add(new RowSorter.SortKey(UnitAssignmentTableModel.COL_UNIT, SortOrder.DESCENDING));
+ ArrayList unitSortKeys = new ArrayList<>();
+ unitSortKeys.add(new SortKey(UnitAssignmentTableModel.COL_UNIT, SortOrder.DESCENDING));
+ sortKeys.add(new SortKey(UnitAssignmentTableModel.COL_UNIT, SortOrder.DESCENDING));
unitSorter.setSortKeys(unitSortKeys);
TableColumn column;
for (int i = 0; i < UnitAssignmentTableModel.N_COL; i++) {
@@ -348,7 +354,7 @@ private void initComponents(boolean doRetirement) {
column.setCellRenderer(unitModel.getRenderer(i));
}
- unitAssignmentTable.setRowHeight(80);
+ unitAssignmentTable.setRowHeight(50);
unitAssignmentTable.setIntercellSpacing(new Dimension(0, 0));
unitAssignmentTable.setShowGrid(false);
unitAssignmentTable.getSelectionModel().addListSelectionListener(ev -> enableAddRemoveButtons());
@@ -423,7 +429,7 @@ private void setUserPreferences(boolean doRetirement) {
}
}
- public ActionListener buttonListener = new ActionListener() {
+ final private ActionListener buttonListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent ev) {
if (ev.getSource().equals(btnRoll)) {
@@ -433,7 +439,7 @@ public void actionPerformed(ActionEvent ev) {
}
if (miscModifier(id) != 0) {
- targetRolls.get(id).addModifier(miscModifier(id), "Misc");
+ targetRolls.get(id).addModifier(miscModifier(id), "Custom");
}
}
rdTracker.rollRetirement(contract, targetRolls,
@@ -472,7 +478,6 @@ public void actionPerformed(ActionEvent ev) {
};
private void initResults() {
- /* Find unassigned units that can be stolen */
List unassignedMechs = new ArrayList<>();
List unassignedASF = new ArrayList<>();
ArrayList availableUnits = new ArrayList<>();
@@ -493,47 +498,10 @@ private void initResults() {
}
});
- /* Defectors who steal a unit will take either the one they were
- * piloting or one of the unassigned units (50/50, unless there
- * is only one choice)
- */
for (UUID id : rdTracker.getRetirees(contract)) {
Person p = hqView.getCampaign().getPerson(id);
- if (rdTracker.getPayout(id).hasStolenUnit()) {
- boolean unassignedAvailable =
- (!unassignedMechs.isEmpty() && p.getPrimaryRole().isMechWarrior())
- || (!unassignedASF.isEmpty() && p.getPrimaryRole().isAerospacePilot());
- /*
- * If a unit has previously been assigned, check that it is still available
- * and either assigned to the current player or unassigned. If so, keep
- * the previous value.
- */
- if ((null != rdTracker.getPayout(id).getStolenUnitId())
- && (null != hqView.getCampaign().getUnit(rdTracker.getPayout(id).getStolenUnitId()))
- && p.equals(hqView.getCampaign().getUnit(rdTracker.getPayout(id).getStolenUnitId()).getCommander())) {
- continue;
- }
-
- if ((p.getUnit() != null) && ((Compute.d6() < 4) || !unassignedAvailable)) {
- unitAssignments.put(id, p.getUnit().getId());
- } else if (unassignedAvailable) {
- if (p.getPrimaryRole().isMechWarrior()) {
- int roll = Compute.randomInt(unassignedMechs.size());
- unitAssignments.put(id, unassignedMechs.get(roll));
- rdTracker.getPayout(id).setStolenUnitId(unassignedMechs.get(roll));
- availableUnits.remove(unassignedMechs.get(roll));
- unassignedMechs.remove(roll);
- } else if (p.getPrimaryRole().isAerospacePilot()) {
- int roll = Compute.randomInt(unassignedASF.size());
- unitAssignments.put(id, unassignedASF.get(roll));
- rdTracker.getPayout(id).setStolenUnitId(unassignedASF.get(roll));
- availableUnits.remove(unassignedASF.get(roll));
- unassignedASF.remove(roll);
- }
- }
- }
/* Retirees who brought a unit will take the same unit when
- * they go if it is still around and has not been stolen.
+ * they go if it is still around
*/
if (hqView.getCampaign().getCampaignOptions().isTrackOriginalUnit()
&& (null != p.getOriginalUnitId())
@@ -551,27 +519,14 @@ private void initResults() {
rdTracker.getPayout(id).setPayoutAmount(temp);
}
}
- /*
- * For infantry, the unit commander makes a retirement roll on behalf of the
- * entire unit. Unassigned infantry can retire individually.
- */
+
if ((p.getUnit() != null) && p.getPrimaryRole().isSoldierOrBattleArmour()) {
unitAssignments.put(id, p.getUnit().getId());
}
((UnitAssignmentTableModel) unitAssignmentTable.getModel()).setData(availableUnits);
}
- ArrayList retireeList = new ArrayList<>();
- boolean showRecruitColumn = false;
- for (UUID pid : rdTracker.getRetirees(contract)) {
- retireeList.add(pid);
- if ((hqView.getCampaign().getRetirementDefectionTracker().getPayout(pid).getDependents() > 0)
- || hqView.getCampaign().getRetirementDefectionTracker().getPayout(pid).hasHeir()
- || hqView.getCampaign().getRetirementDefectionTracker().getPayout(pid).hasRecruit()) {
- showRecruitColumn = true;
- }
- }
- ((XTableColumnModel) retireeTable.getColumnModel()).setColumnVisible(retireeTable.getColumnModel().getColumn(retireeTable.convertColumnIndexToView(RetirementTableModel.COL_RECRUIT)), !showRecruitColumn);
+ ArrayList retireeList = new ArrayList<>(rdTracker.getRetirees(contract));
((RetirementTableModel) retireeTable.getModel()).setData(retireeList, unitAssignments);
filterPersonnel(retireeSorter, cbGroupResults, true);
lblPayment.setText(totalPayout().toAmountAndSymbolString());
@@ -637,12 +592,19 @@ public boolean include(Entry extends UnitAssignmentTableModel, ? extends Integ
public static int weightClassIndex(Unit u) {
int retVal = u.getEntity().getWeightClass();
- if (u.getEntity().isClan() || (u.getEntity().getTechLevel() > megamek.common.TechConstants.T_INTRO_BOXSET)) {
+
+ if (u.getEntity().isClan()) {
+ retVal++;
+ }
+
+ if ((u.getEntity().getTechLevel() > TechConstants.T_INTRO_BOXSET)) {
retVal++;
}
+
if (!u.isFunctional()) {
- retVal--;
+ retVal = retVal / 2;
}
+
return Math.max(0, retVal);
}
@@ -650,16 +612,21 @@ public Money totalPayout() {
if (null == rdTracker.getRetirees(contract)) {
return Money.zero();
}
+
Money retVal = Money.zero();
+
for (UUID id : rdTracker.getRetirees(contract)) {
- if (null == rdTracker.getPayout(id)) {
+ if (rdTracker.getPayout(id) == null) {
continue;
}
+
if (((RetirementTableModel) retireeTable.getModel()).getAltPayout().containsKey(id)) {
retVal = retVal.plus(((RetirementTableModel) retireeTable.getModel()).getAltPayout().get(id));
continue;
}
+
Money payout = rdTracker.getPayout(id).getPayoutAmount();
+
/* If no unit is required as part of the payout, the unit is part or all of the
* final payout.
*/
@@ -667,34 +634,42 @@ public Money totalPayout() {
null != unitAssignments.get(id) &&
null != hqView.getCampaign().getUnit(unitAssignments.get(id)))) {
payout = payout.minus(hqView.getCampaign().getUnit(unitAssignments.get(id)).getBuyCost());
- } else if ((hqView.getCampaign().getCampaignOptions().isUseShareSystem() &&
- hqView.getCampaign().getCampaignOptions().isTrackOriginalUnit() &&
- hqView.getCampaign().getPerson(id).getOriginalUnitId() == unitAssignments.get(id)) &&
- null != hqView.getCampaign().getUnit(unitAssignments.get(id))) {
+ } else if ((hqView.getCampaign().getCampaignOptions().isUseShareSystem()
+ && hqView.getCampaign().getCampaignOptions().isTrackOriginalUnit()
+ && Objects.equals(hqView.getCampaign().getPerson(id).getOriginalUnitId(), unitAssignments.get(id)))
+ && hqView.getCampaign().getUnit(unitAssignments.get(id)) != null) {
payout = payout.minus(hqView.getCampaign().getUnit(unitAssignments.get(id)).getBuyCost());
}
+
/* If using the share system and tracking the original unit,
* the payout is also reduced by the value of the unit.
*/
- if (hqView.getCampaign().getCampaignOptions().isUseShareSystem() &&
- hqView.getCampaign().getCampaignOptions().isTrackOriginalUnit() &&
- hqView.getCampaign().getPerson(id).getOriginalUnitId() == unitAssignments.get(id) &&
- null != hqView.getCampaign().getUnit(unitAssignments.get(id))) {
+ if (hqView.getCampaign().getCampaignOptions().isUseShareSystem()
+ && hqView.getCampaign().getCampaignOptions().isTrackOriginalUnit()
+ && Objects.equals(hqView.getCampaign().getPerson(id).getOriginalUnitId(), unitAssignments.get(id))
+ && hqView.getCampaign().getUnit(unitAssignments.get(id)) != null) {
payout = payout.minus(hqView.getCampaign().getUnit(unitAssignments.get(id)).getBuyCost());
}
- /* If the unit given in payment is of lower quality than required, pay
- * an additional 3M C-bills per class.
- */
- if (null != unitAssignments.get(id)) {
- payout = payout.plus(getShortfallAdjustment(
- rdTracker.getPayout(id).getWeightClass(),
- RetirementDefectionDialog.weightClassIndex(hqView.getCampaign().getUnit(unitAssignments.get(id)))));
- }
- /* If the pilot has stolen a unit, there is no payout */
- if (rdTracker.getPayout(id).hasStolenUnit() && (null != unitAssignments.get(id))) {
- payout = Money.zero();
+
+ // If the person is still under contract, we don't care that they're owed a unit
+ if (!isBreakingContract(
+ hqView.getCampaign().getPerson(id),
+ hqView.getCampaign().getLocalDate(),
+ hqView.getCampaign().getCampaignOptions().getServiceContractDuration())) {
+ // If the unit given in payment is of lower quality than required, pay an additional 3M C-bills per class.
+ // If the person is breaking contract, they waive this compensation
+ if (null != unitAssignments.get(id)) {
+ payout = payout.plus(getShortfallAdjustment(rdTracker.getPayout(id).getWeightClass(),
+ RetirementDefectionDialog.weightClassIndex(hqView.getCampaign().getUnit(unitAssignments.get(id)))));
+ }
+
+ // if a unit is required, but none given, pay an additional 3M c-bills per class
+ if (unitAssignments.get(id) == null) {
+ payout = payout.plus(getShortfallAdjustment(rdTracker.getPayout(id).getWeightClass(), 0));
+ }
}
- // If the payout is negative just set it to zero
+
+ // If the payout is negative, set it to zero
if (payout.isNegative()) {
payout = Money.zero();
}
@@ -724,7 +699,7 @@ private Money getTotalBonus() {
Money retVal = Money.zero();
for (UUID id : targetRolls.keySet()) {
if (((RetirementTableModel) personnelTable.getModel()).getPayBonus(id)) {
- retVal = retVal.plus(RetirementDefectionTracker.getBonusCost(hqView.getCampaign(),
+ retVal = retVal.plus(RetirementDefectionTracker.getPayoutOrBonusValue(hqView.getCampaign(),
hqView.getCampaign().getPerson(id)));
}
}
@@ -745,10 +720,6 @@ public static Money getShortfallAdjustment(int required, int actual) {
}
}
- public UUID getUnitId(UUID pid) {
- return unitAssignments.get(pid);
- }
-
public Map getUnitAssignments() {
return unitAssignments;
}
@@ -758,8 +729,17 @@ public boolean wasAborted() {
}
private boolean unitAssignmentsComplete() {
- return rdTracker.getRetirees(contract).stream()
- .noneMatch(id -> (rdTracker.getPayout(id).getWeightClass() > 0) && !unitAssignments.containsKey(id));
+ if (unitAssignmentTable.getModel().getRowCount() <= 0) {
+ return true;
+ }
+
+ return rdTracker.getRetirees().stream()
+ .filter(uuid -> isBreakingContract(hqView.getCampaign().getPerson(uuid),
+ hqView.getCampaign().getLocalDate(),
+ hqView.getCampaign().getCampaignOptions().getServiceContractDuration()))
+ .findFirst()
+ .map(uuid -> (!unitAssignments.containsKey(uuid)))
+ .orElse(true);
}
private void enableAddRemoveButtons() {
@@ -769,9 +749,9 @@ private void enableAddRemoveButtons() {
} else {
int retireeRow = retireeTable.convertRowIndexToModel(retireeTable.getSelectedRow());
UUID pid = ((RetirementTableModel)(retireeTable.getModel())).getPerson(retireeRow).getId();
- if (null != rdTracker.getPayout(pid) &&
- rdTracker.getPayout(pid).hasStolenUnit() &&
- !btnEdit.isSelected()) {
+ if (isBreakingContract(hqView.getCampaign().getPerson(pid),
+ hqView.getCampaign().getLocalDate(),
+ hqView.getCampaign().getCampaignOptions().getServiceContractDuration())) {
btnAddUnit.setEnabled(false);
btnRemoveUnit.setEnabled(false);
} else if (hqView.getCampaign().getPerson(pid).getPrimaryRole().isSoldierOrBattleArmour()) {
@@ -779,13 +759,9 @@ private void enableAddRemoveButtons() {
btnRemoveUnit.setEnabled(false);
} else if (unitAssignments.containsKey(pid)) {
btnAddUnit.setEnabled(false);
- if ((hqView.getCampaign().getCampaignOptions().isTrackOriginalUnit() &&
- unitAssignments.get(pid).equals(hqView.getCampaign().getPerson(pid).getOriginalUnitId())) &&
- !btnEdit.isSelected()) {
- btnRemoveUnit.setEnabled(false);
- } else {
- btnRemoveUnit.setEnabled(true);
- }
+ btnRemoveUnit.setEnabled((!hqView.getCampaign().getCampaignOptions().isTrackOriginalUnit()
+ || !unitAssignments.get(pid).equals(hqView.getCampaign().getPerson(pid).getOriginalUnitId()))
+ || btnEdit.isSelected());
} else if (null != rdTracker.getPayout(pid) &&
rdTracker.getPayout(pid).getWeightClass() > 0) {
if (unitAssignmentTable.getSelectedRow() < 0) {
@@ -868,11 +844,11 @@ private void setUnitGroup() {
class RetirementTable extends JTable {
private static class SpinnerEditor extends AbstractCellEditor implements TableCellEditor {
- private JSpinner spinner;
+ final private JSpinner spinner;
public SpinnerEditor() {
spinner = new JSpinner(new SpinnerNumberModel(0, -10, 10, 1));
- ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().setEditable(false);
+ ((DefaultEditor) spinner.getEditor()).getTextField().setEditable(false);
}
@Override
@@ -904,7 +880,7 @@ public RetirementTable(RetirementTableModel model, CampaignGUI hqView) {
}
}
- setRowHeight(80);
+ setRowHeight(50);
setIntercellSpacing(new Dimension(0, 0));
setShowGrid(false);
@@ -913,10 +889,6 @@ public RetirementTable(RetirementTableModel model, CampaignGUI hqView) {
getColumnModel().getColumn(convertColumnIndexToView(RetirementTableModel.COL_MISC_MOD))
.setCellEditor(new SpinnerEditor());
-
- JComboBox cbRecruitRole = new JComboBox<>(PersonnelRole.values());
- getColumnModel().getColumn(convertColumnIndexToView(RetirementTableModel.COL_RECRUIT))
- .setCellEditor(new DefaultCellEditor(cbRecruitRole));
}
public void setGeneralMod(int mod) {
diff --git a/MekHQ/src/mekhq/gui/enums/PersonnelFilter.java b/MekHQ/src/mekhq/gui/enums/PersonnelFilter.java
index b6693db0d0..fc9d2a3ca0 100644
--- a/MekHQ/src/mekhq/gui/enums/PersonnelFilter.java
+++ b/MekHQ/src/mekhq/gui/enums/PersonnelFilter.java
@@ -77,8 +77,11 @@ public enum PersonnelFilter {
KIDS("PersonnelFilter.KIDS.text", "PersonnelFilter.KIDS.toolTipText"),
PRISONER("PersonnelFilter.PRISONER.text", "PersonnelFilter.PRISONER.toolTipText", false, false),
INACTIVE("PersonnelFilter.INACTIVE.text", "PersonnelFilter.INACTIVE.toolTipText", false, false),
+ ON_LEAVE("PersonnelFilter.ON_LEAVE.text", "PersonnelFilter.ON_LEAVE.toolTipText", false, false),
MIA("PersonnelFilter.MIA.text", "PersonnelFilter.MIA.toolTipText", false, false),
RETIRED("PersonnelFilter.RETIRED.text", "PersonnelFilter.RETIRED.toolTipText", false, false),
+ RESIGNED("PersonnelFilter.RESIGNED.text", "PersonnelFilter.RESIGNED.toolTipText", false, false),
+ AWOL("PersonnelFilter.AWOL.text", "PersonnelFilter.AWOL.toolTipText", false, false),
DESERTED("PersonnelFilter.DESERTED.text", "PersonnelFilter.DESERTED.toolTipText", false, false),
STUDENT("PersonnelFilter.STUDENT.text", "PersonnelFilter.STUDENT.toolTipText", false, false),
MISSING("PersonnelFilter.MISSING.text", "PersonnelFilter.MISSING.toolTipText", false, false),
@@ -481,15 +484,21 @@ public boolean getFilteredInformation(final Person person, LocalDate currentDate
case FOUNDER:
return person.isFounder();
case KIDS:
- return person.isChild(currentDate);
+ return ((person.isChild(currentDate)) && (!person.getStatus().isLeft()));
case PRISONER:
- return person.getPrisonerStatus().isCurrentPrisoner() || person.getPrisonerStatus().isBondsman();
+ return ((person.getPrisonerStatus().isCurrentPrisoner()) || (person.getPrisonerStatus().isBondsman()));
case INACTIVE:
return !person.getStatus().isActive();
+ case ON_LEAVE:
+ return person.getStatus().isOnLeave();
case MIA:
return person.getStatus().isMIA();
case RETIRED:
return person.getStatus().isRetired();
+ case RESIGNED:
+ return ((person.getStatus().isResigned()) || (person.getStatus().isLeft()));
+ case AWOL:
+ return person.getStatus().isAwol();
case DESERTED:
return person.getStatus().isDeserted();
case STUDENT:
diff --git a/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java b/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java
index e34134e220..5459181e5b 100644
--- a/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java
+++ b/MekHQ/src/mekhq/gui/enums/PersonnelTableModelColumn.java
@@ -105,6 +105,7 @@ public enum PersonnelTableModelColumn {
TRYING_TO_CONCEIVE("PersonnelTableModelColumn.TRYING_TO_CONCEIVE.text"),
IMMORTAL("PersonnelTableModelColumn.IMMORTAL.text"),
TOUGHNESS("PersonnelTableModelColumn.TOUGHNESS.text"),
+ FATIGUE("PersonnelTableModelColumn.FATIGUE.text"),
EDGE("PersonnelTableModelColumn.EDGE.text"),
SPA_COUNT("PersonnelTableModelColumn.SPA_COUNT.text"),
IMPLANT_COUNT("PersonnelTableModelColumn.IMPLANT_COUNT.text"),
@@ -368,6 +369,10 @@ public boolean isToughness() {
return this == TOUGHNESS;
}
+ public boolean isFatigue() {
+ return this == FATIGUE;
+ }
+
public boolean isEdge() {
return this == EDGE;
}
@@ -658,6 +663,8 @@ public String getCellValue(final Campaign campaign, final PersonnelMarket person
return resources.getString(person.getStatus().isDead() ? "NA.text" : (person.isImmortal() ? "Yes.text" : "No.text"));
case TOUGHNESS:
return Integer.toString(person.getToughness());
+ case FATIGUE:
+ return Integer.toString(person.getFatigue());
case EDGE:
return Integer.toString(person.getEdge());
case SPA_COUNT:
@@ -943,6 +950,7 @@ public boolean isVisible(final Campaign campaign, final PersonnelTabView view,
case FIRST_NAME:
case LAST_NAME:
case TOUGHNESS:
+ case FATIGUE:
case EDGE:
case SPA_COUNT:
case IMPLANT_COUNT:
diff --git a/MekHQ/src/mekhq/gui/model/RetirementTableModel.java b/MekHQ/src/mekhq/gui/model/RetirementTableModel.java
index 08a34bc382..aa9cbe43f6 100644
--- a/MekHQ/src/mekhq/gui/model/RetirementTableModel.java
+++ b/MekHQ/src/mekhq/gui/model/RetirementTableModel.java
@@ -2,13 +2,11 @@
import megamek.codeUtilities.ObjectUtility;
import megamek.common.*;
-import mekhq.MekHQ;
import mekhq.campaign.Campaign;
import mekhq.campaign.finances.Money;
import mekhq.campaign.force.Force;
import mekhq.campaign.personnel.Person;
-import mekhq.campaign.personnel.RetirementDefectionTracker;
-import mekhq.campaign.personnel.enums.PersonnelRole;
+import mekhq.campaign.personnel.turnoverAndRetention.RetirementDefectionTracker;
import mekhq.campaign.unit.Unit;
import mekhq.gui.BasicInfo;
import mekhq.gui.dialog.RetirementDefectionDialog;
@@ -19,6 +17,7 @@
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
+import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.*;
@@ -32,14 +31,13 @@ public class RetirementTableModel extends AbstractTableModel {
public final static int COL_PAY_BONUS = 6;
public final static int COL_MISC_MOD = 7;
public final static int COL_PAYOUT = 8;
- public final static int COL_RECRUIT = 9;
- public final static int COL_UNIT = 10;
- public final static int N_COL = 11;
+ public final static int COL_UNIT = 9;
+ public final static int N_COL = 10;
private final static String[] colNames = {
- "Person", "Assignment", "Force", "Target",
- "Shares", "Bonus Cost", "Pay Bonus", "Misc Modifier",
- "Payout", "Recruit", "Unit"
+ "Person", "Assignment", "Force", "Target Number",
+ "Shares", "Retention Bonus", "Pay Bonus", "Custom Modifier",
+ "Payout", "Unit"
};
private final Campaign campaign;
@@ -74,7 +72,7 @@ public void setData(Map targets) {
data.clear();
for (UUID id : targets.keySet()) {
data.add(id);
- payBonus.put(id, false);
+ payBonus.put(id, campaign.getCampaignOptions().isPayBonusDefault());
miscMods.put(id, 0);
}
fireTableDataChanged();
@@ -101,11 +99,9 @@ public int getColumnWidth(int c) {
case COL_ASSIGN:
case COL_FORCE:
case COL_UNIT:
- case COL_RECRUIT:
- return 125;
+ return 70;
case COL_BONUS_COST:
case COL_PAYOUT:
- return 70;
case COL_TARGET:
case COL_SHARES:
case COL_MISC_MOD:
@@ -119,14 +115,12 @@ public int getColumnWidth(int c) {
public int getAlignment(int col) {
switch (col) {
case COL_PERSON:
+ return SwingConstants.LEFT;
case COL_ASSIGN:
case COL_FORCE:
case COL_UNIT:
- case COL_RECRUIT:
- return SwingConstants.LEFT;
case COL_BONUS_COST:
case COL_PAYOUT:
- return SwingConstants.RIGHT;
case COL_TARGET:
case COL_PAY_BONUS:
case COL_SHARES:
@@ -144,8 +138,6 @@ public boolean isCellEditable(int row, int col) {
case COL_PAY_BONUS:
case COL_MISC_MOD:
return true;
- case COL_RECRUIT:
- return campaign.getRetirementDefectionTracker().getPayout(data.get(row)).hasRecruit();
default:
return false;
}
@@ -205,7 +197,7 @@ public Object getValueAt(int row, int col) {
return u.getName() + " (" + p.getMaintenanceTimeUsing() + "m)";
}
} else {
- return "" + p.getTechUnits().size() + " units (" + p.getMaintenanceTimeUsing() + "m)";
+ return p.getTechUnits().size() + " units (" + p.getMaintenanceTimeUsing() + "m)";
}
}
return "-";
@@ -224,7 +216,7 @@ public Object getValueAt(int row, int col) {
(payBonus.get(p.getId()) ? 2 : 0) +
miscMods.get(p.getId()) + generalMod;
case COL_BONUS_COST:
- return RetirementDefectionTracker.getBonusCost(campaign, p).toAmountAndSymbolString();
+ return RetirementDefectionTracker.getPayoutOrBonusValue(campaign, p).toAmountAndSymbolString();
case COL_PAY_BONUS:
return payBonus.getOrDefault(p.getId(), false);
case COL_MISC_MOD:
@@ -246,21 +238,31 @@ public Object getValueAt(int row, int col) {
if ((campaign.getRetirementDefectionTracker().getPayout(p.getId()).getWeightClass() == 0 &&
null != unitAssignments.get(p.getId())) ||
(campaign.getCampaignOptions().isUseShareSystem() &&
- campaign.getCampaignOptions().isTrackOriginalUnit() &&
- p.getOriginalUnitId() == unitAssignments.get(p.getId()) &&
+ campaign.getCampaignOptions().isTrackOriginalUnit()
+ && Objects.equals(p.getOriginalUnitId(), unitAssignments.get(p.getId())) &&
null != campaign.getUnit(unitAssignments.get(p.getId())))) {
payout = payout.minus(campaign.getUnit(unitAssignments.get(p.getId())).getBuyCost());
}
- if (null != unitAssignments.get(p.getId())) {
- payout = payout.plus(RetirementDefectionDialog.getShortfallAdjustment(campaign.getRetirementDefectionTracker().getPayout(p.getId()).getWeightClass(),
- RetirementDefectionDialog.weightClassIndex(campaign.getUnit(unitAssignments.get(p.getId())))));
- }
- /* No payout if the pilot stole a unit */
- if (campaign.getRetirementDefectionTracker().getPayout(p.getId()).hasStolenUnit() &&
- null != unitAssignments.get(p.getId())) {
- payout = Money.zero();
+
+ // if the person is under contract we don't check whether they need a unit or are owed a shortfall
+ if (ChronoUnit.MONTHS.between(p.getRecruitment(), campaign.getLocalDate())
+ >= campaign.getCampaignOptions().getServiceContractDuration()) {
+ // if the person requires a unit, check to ensure there isn't a shortfall
+ if (null != unitAssignments.get(p.getId())) {
+ payout = payout.plus(RetirementDefectionDialog.getShortfallAdjustment(campaign.getRetirementDefectionTracker().getPayout(p.getId()).getWeightClass(),
+ RetirementDefectionDialog.weightClassIndex(campaign.getUnit(unitAssignments.get(p.getId())))));
+ }
+
+ // if the person requires a unit, but doesn't have one...
+ if ((unitAssignments.get(p.getId()) == null) && (campaign.getRetirementDefectionTracker().getPayout(p.getId()).getWeightClass() > 0)) {
+ payout = payout.plus(RetirementDefectionDialog.getShortfallAdjustment(
+ campaign.getRetirementDefectionTracker().getPayout(p.getId()).getWeightClass(),
+ 0)
+ );
+ }
}
- // If payout is negative then just make it zero
+
+ // If payout is negative then make it zero
if (payout.isNegative()) {
payout = Money.zero();
}
@@ -276,20 +278,6 @@ public Object getValueAt(int row, int col) {
} else {
return "Class " + campaign.getRetirementDefectionTracker().getPayout(p.getId()).getWeightClass();
}
- case COL_RECRUIT:
- RetirementDefectionTracker.Payout pay =
- campaign.getRetirementDefectionTracker().getPayout(data.get(row));
- if (null == pay) {
- return "";
- } else if (pay.getDependents() > 0) {
- return pay.getDependents() + " Dependents";
- } else if (pay.hasRecruit()) {
- return pay.getRecruitRole().getName(campaign.getFaction().isClan());
- } else if (pay.hasHeir()) {
- return "Heir";
- } else {
- return "";
- }
default:
return "?";
}
@@ -316,13 +304,6 @@ public void setValueAt(Object value, int row, int col) {
if (null != value) {
unitAssignments.put(getPerson(row).getId(), (UUID) value);
}
- } else if (col == COL_RECRUIT) {
- for (PersonnelRole role : PersonnelRole.values()) {
- if (role.getName(campaign.getFaction().isClan()).equals(value)) {
- campaign.getRetirementDefectionTracker().getPayout(data.get(row)).setRecruitRole(role);
- break;
- }
- }
}
fireTableDataChanged();
}
@@ -366,17 +347,9 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
- int actualRow = table.convertRowIndexToModel(row);
int actualCol = table.convertColumnIndexToModel(column);
- Person p = getPerson(actualRow);
setHorizontalAlignment(getAlignment(actualCol));
- if (!isSelected) {
- if (null != campaign.getRetirementDefectionTracker().getPayout(p.getId()) &&
- campaign.getRetirementDefectionTracker().getPayout(p.getId()).getWeightClass() > 0) {
- setForeground(MekHQ.getMHQOptions().getPaidRetirementForeground());
- setBackground(MekHQ.getMHQOptions().getPaidRetirementBackground());
- }
- }
+
return this;
}
}
@@ -395,7 +368,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
setText(getValueAt(actualRow, actualCol).toString());
if (actualCol == COL_PERSON) {
setText(p.getFullDesc(campaign));
- setImage(p.getPortrait().getImage(54));
+ setImage(p.getPortrait().getImage(40));
} else if (actualCol == COL_ASSIGN) {
Unit u = p.getUnit();
if (!p.getTechUnits().isEmpty()) {
@@ -406,9 +379,9 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
String desc = "" + u.getName() + "
";
desc += u.getEntity().getWeightClassName();
if (!((u.getEntity() instanceof SmallCraft) || (u.getEntity() instanceof Jumpship))) {
- desc += " " + UnitType.getTypeDisplayableName(u.getEntity().getUnitType());
+ desc += ' ' + UnitType.getTypeDisplayableName(u.getEntity().getUnitType());
}
- desc += "
" + u.getStatus() + "";
+ desc += "
" + u.getStatus();
setText(desc);
Image mekImage = u.getImage(this);
if (null != mekImage) {
@@ -422,18 +395,18 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
} else if (actualCol == COL_FORCE) {
Force force = campaign.getForceFor(p);
if (null != force) {
- String desc = "" + force.getName() + "";
+ StringBuilder desc = new StringBuilder("" + force.getName() + "");
Force parent = force.getParentForce();
//cut off after three lines and don't include the top level
int lines = 1;
while ((parent != null) && (null != parent.getParentForce()) && (lines < 4)) {
- desc += "
" + parent.getName();
+ desc.append("
").append(parent.getName());
lines++;
parent = parent.getParentForce();
}
- desc += "";
- setHtmlText(desc);
- final Image forceImage = force.getForceIcon().getImage(54);
+ desc.append("");
+ setHtmlText(desc.toString());
+ final Image forceImage = force.getForceIcon().getImage(40);
if (null != forceImage) {
setImage(forceImage);
} else {
@@ -445,13 +418,6 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
}
MekHqTableCellRenderer.setupTableColors(this, table, isSelected, hasFocus, row);
- if (!isSelected) {
- if ((campaign.getRetirementDefectionTracker().getPayout(p.getId()) != null)
- && (campaign.getRetirementDefectionTracker().getPayout(p.getId()).getWeightClass() > 0)) {
- setForeground(MekHQ.getMHQOptions().getPaidRetirementForeground());
- setBackground(MekHQ.getMHQOptions().getPaidRetirementBackground());
- }
- }
return this;
}
diff --git a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java
index b741d7bd38..1e31aa74b2 100644
--- a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java
+++ b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java
@@ -255,20 +255,97 @@ public class CampaignOptionsPane extends AbstractMHQTabbedPane {
private JSpinner[] spnBaseSalary;
//endregion Personnel Tab
- //region Life Paths Ta
- // Personnel Randomization
- private JCheckBox chkUseDylansRandomXP;
- private RandomOriginOptionsPanel randomOriginOptionsPanel;
-
- // Retirement
+ //region Turnover and Retention Tab
+ // Header Options
private JCheckBox chkUseRetirementDateTracking;
- private JPanel randomRetirementPanel;
- private MMComboBox comboRandomRetirementMethod;
- private JCheckBox chkUseYearEndRandomRetirement;
+ private JCheckBox chkUseRandomRetirement;
+
+ // Settings
+ private JPanel turnoverAndRetentionSettingsPanel = new JPanel();
+ private JLabel lblTurnoverTargetNumberMethod;
+ private MMComboBox comboTurnoverTargetNumberMethod;
+ private JLabel lblTurnoverDifficulty;
+ private MMComboBox comboTurnoverDifficulty;
+ private JLabel lblTurnoverFixedTargetNumber;
+ private JSpinner spnTurnoverFixedTargetNumber;
+ private JLabel lblTurnoverFrequency;
+ private MMComboBox comboTurnoverFrequency;
private JCheckBox chkUseContractCompletionRandomRetirement;
+ private JCheckBox chkUseRandomFounderTurnover;
+ private JCheckBox chkUseFounderRetirement;
+ private JCheckBox chkAeroRecruitsHaveUnits;
+ private JCheckBox chkTrackOriginalUnit;
+ private JCheckBox chkUseSubContractSoldiers;
+ private JLabel lblServiceContractDuration;
+ private JSpinner spnServiceContractDuration;
+ private JLabel lblServiceContractModifier;
+ private JSpinner spnServiceContractModifier;
+ private JCheckBox chkPayBonusDefault;
+
+ // Modifiers
+ private JPanel turnoverAndRetentionModifiersPanel = new JPanel();
private JCheckBox chkUseCustomRetirementModifiers;
- private JCheckBox chkUseRandomFounderRetirement;
- private JCheckBox chkTrackUnitFatigue;
+ private JCheckBox chkUseFatigueModifiers;
+ private JCheckBox chkUseSkillModifiers;
+ private JCheckBox chkUseAgeModifiers;
+ private JCheckBox chkUseUnitRatingModifiers;
+ private JCheckBox chkUseFactionModifiers;
+ private JCheckBox chkUseMissionStatusModifiers;
+ private JCheckBox chkUseMarriageModifiers;
+ private JCheckBox chkUseLoyaltyModifiers;
+
+ private JPanel loyaltySubPanel = new JPanel();
+ private JCheckBox chkUseHideLoyalty;
+
+ // Payout
+ private JPanel turnoverAndRetentionPayoutPanel = new JPanel();
+ private JLabel lblPayoutRateOfficer;
+ private JSpinner spnPayoutRateOfficer;
+ private JLabel lblPayoutRateEnlisted;
+ private JSpinner spnPayoutRateEnlisted;
+ private JLabel lblPayoutRetirementMultiplier;
+ private JSpinner spnPayoutRetirementMultiplier;
+ private JCheckBox chkUsePayoutServiceBonus;
+
+ private JPanel payoutServiceBonusSubPanel = new JPanel();
+ private JLabel lblPayoutServiceBonusRate;
+ private JSpinner spnPayoutServiceBonusRate;
+
+ // Unit Cohesion
+ private JPanel turnoverAndRetentionUnitCohesionPanel = new JPanel();
+ private JCheckBox chkUseAdministrativeStrain;
+ private JCheckBox chkUseManagementSkill;
+
+ private JPanel administrativeStrainSubPanel = new JPanel();
+ private JLabel lblAdministrativeCapacity;
+ private JSpinner spnAdministrativeCapacity;
+ private JLabel lblMultiCrewStrainDivider;
+ private JSpinner spnMultiCrewStrainDivider;
+
+ private JPanel managementSkillSubPanel = new JPanel();
+ private JCheckBox chkUseCommanderLeadershipOnly;
+ private JLabel lblManagementSkillPenalty;
+ private JSpinner spnManagementSkillPenalty;
+
+ // Fatigue
+ private JPanel turnoverAndRetentionFatiguePanel = new JPanel();
+ private JCheckBox chkUseFatigue;
+
+ private JPanel fatigueSubPanel = new JPanel();
+ private JLabel lblFatigueWarning;
+ private JLabel lblFatigueRate;
+ private JSpinner spnFatigueRate;
+ private JCheckBox chkUseInjuryFatigue;
+ private JLabel lblFieldKitchenCapacity;
+ private JSpinner spnFieldKitchenCapacity;
+ private JLabel lblFatigueLeaveThreshold;
+ private JSpinner spnFatigueLeaveThreshold;
+ //endregion Turnover and Retention Tab
+
+ //region Life Paths Tab
+ // Personnel Randomization
+ private JCheckBox chkUseDylansRandomXP;
+ private RandomOriginOptionsPanel randomOriginOptionsPanel;
// Marriage
private JCheckBox chkUseManualMarriages;
@@ -432,6 +509,12 @@ public class CampaignOptionsPane extends AbstractMHQTabbedPane {
private JSpinner spnDamagedPartsValueMultiplier;
private JSpinner spnUnrepairablePartsValueMultiplier;
private JSpinner spnCancelledOrderRefundMultiplier;
+
+ private JPanel sharesPanel;
+ private JCheckBox chkUseShareSystem;
+ private JPanel sharesSubPanel;
+ private JCheckBox chkSharesExcludeLargeCraft;
+ private JCheckBox chkSharesForAll;
//endregion Finances Tab
//region Mercenary Tab
@@ -547,12 +630,6 @@ public class CampaignOptionsPane extends AbstractMHQTabbedPane {
private MMComboBox comboSkillLevel;
// unit administration
- private JCheckBox chkUseShareSystem;
- private JCheckBox chkSharesExcludeLargeCraft;
- private JCheckBox chkSharesForAll;
- private JCheckBox chkAeroRecruitsHaveUnits;
- private JCheckBox chkUseLeadership;
- private JCheckBox chkTrackOriginalUnit;
private JCheckBox chkUseAero;
private JCheckBox chkUseVehicles;
private JCheckBox chkClanVehicles;
@@ -637,6 +714,7 @@ protected void initialize() {
addTab(resources.getString("techLimitsPanel.title"), createTechLimitsTab());
addTab(resources.getString("personnelPanel.title"), createPersonnelTab());
addTab(resources.getString("lifePathsPanel.title"), createLifePathsPanel());
+ addTab(resources.getString("turnoverAndRetentionPanel.title"), createTurnoverAndRetentionTab());
addTab(resources.getString("financesPanel.title"), createFinancesTab(campaign.getCampaignOptions().isReverseQualityNames()));
addTab(resources.getString("mercenaryPanel.title"), createMercenaryTab());
addTab(resources.getString("experiencePanel.title"), createExperienceTab());
@@ -1710,6 +1788,11 @@ public Component getListCellRendererComponent(JList> list, Object value, int i
gridBagConstraints.gridheight = 20;
panFinances.add(createPriceModifiersPanel(reverseQualities), gridBagConstraints);
+ gridBagConstraints.gridx = 4;
+ gridBagConstraints.gridy = 0;
+ gridBagConstraints.gridheight = 20;
+ panFinances.add(createSharesPanel(), gridBagConstraints);
+
return new JScrollPane(panFinances);
}
@@ -2672,14 +2755,6 @@ private JScrollPane createAgainstTheBotTab() {
final boolean enabled = chkUseAtB.isSelected();
enableAtBComponents(panAtB, enabled);
- // This is necessary to prevent issues where disabled options become visible
- if (randomRetirementPanel.isEnabled() != enabled) {
- randomRetirementPanel.setEnabled(enabled);
- if (enabled) {
- comboRandomRetirementMethod.setSelectedItem(comboRandomRetirementMethod.getSelectedItem());
- }
- }
-
if (randomDependentPanel.isEnabled() != enabled) {
randomDependentPanel.setEnabled(enabled);
if (enabled) {
@@ -2737,45 +2812,9 @@ private JScrollPane createAgainstTheBotTab() {
panAtB.add(chkUseStratCon, gridBagConstraints);
// AtB options: "Unit Administration" frame controls
- chkUseShareSystem = new JCheckBox(resources.getString("chkUseShareSystem.text"));
- chkUseShareSystem.setToolTipText(resources.getString("chkUseShareSystem.toolTipText"));
- gridBagConstraints = new GridBagConstraints();
- gridBagConstraints.gridx = 0;
- gridBagConstraints.gridy = 0;
- gridBagConstraints.gridwidth = 1;
- gridBagConstraints.fill = GridBagConstraints.BOTH;
- gridBagConstraints.insets = new Insets(5, 5, 5, 5);
- gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
- panSubAtBAdmin.add(chkUseShareSystem, gridBagConstraints);
-
- chkSharesExcludeLargeCraft = new JCheckBox(resources.getString("chkSharesExcludeLargeCraft.text"));
- chkSharesExcludeLargeCraft.setToolTipText(resources.getString("chkSharesExcludeLargeCraft.toolTipText"));
- gridBagConstraints.gridy++;
- panSubAtBAdmin.add(chkSharesExcludeLargeCraft, gridBagConstraints);
-
- chkSharesForAll = new JCheckBox(resources.getString("chkSharesForAll.text"));
- chkSharesForAll.setToolTipText(resources.getString("chkSharesForAll.toolTipText"));
- gridBagConstraints.gridy++;
- panSubAtBAdmin.add(chkSharesForAll, gridBagConstraints);
-
- chkAeroRecruitsHaveUnits = new JCheckBox(resources.getString("chkAeroRecruitsHaveUnits.text"));
- chkAeroRecruitsHaveUnits.setToolTipText(resources.getString("chkAeroRecruitsHaveUnits.toolTipText"));
- gridBagConstraints.gridy++;
- panSubAtBAdmin.add(chkAeroRecruitsHaveUnits, gridBagConstraints);
-
- chkUseLeadership = new JCheckBox(resources.getString("chkUseLeadership.text"));
- chkUseLeadership.setToolTipText(resources.getString("chkUseLeadership.toolTipText"));
- gridBagConstraints.gridy++;
- panSubAtBAdmin.add(chkUseLeadership, gridBagConstraints);
-
- chkTrackOriginalUnit = new JCheckBox(resources.getString("chkTrackOriginalUnit.text"));
- chkTrackOriginalUnit.setToolTipText(resources.getString("chkTrackOriginalUnit.toolTipText"));
- gridBagConstraints.gridy++;
- panSubAtBAdmin.add(chkTrackOriginalUnit, gridBagConstraints);
-
chkUseAero = new JCheckBox(resources.getString("chkUseAero.text"));
chkUseAero.setToolTipText(resources.getString("chkUseAero.toolTipText"));
- gridBagConstraints.gridy++;
+ gridBagConstraints.gridy = 0;
panSubAtBAdmin.add(chkUseAero, gridBagConstraints);
chkUseVehicles = new JCheckBox(resources.getString("chkUseVehicles.text"));
@@ -3151,7 +3190,7 @@ private JScrollPane createPersonnelTab() {
gbc.gridx++;
personnelPanel.add(createPrisonerPanel(), gbc);
- gbc.gridy++;
+ gbc.gridx++;
personnelPanel.add(createDependentPanel(), gbc);
gbc.gridx = 0;
@@ -3165,7 +3204,43 @@ private JScrollPane createPersonnelTab() {
return scrollPersonnel;
}
- //region Personnel Tab
+ //region Turnover and Retention Tab
+ private JScrollPane createTurnoverAndRetentionTab() {
+ final AbstractMHQScrollablePanel turnoverAndRetentionPanel = new DefaultMHQScrollablePanel(getFrame(),
+ "turnoverAndRetentionPanel", new GridBagLayout());
+ turnoverAndRetentionPanel.setTracksViewportWidth(false);
+
+ final GridBagConstraints gbc = new GridBagConstraints();
+
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.anchor = GridBagConstraints.NORTHWEST;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ turnoverAndRetentionPanel.add(createTurnoverAndRetentionHeaderPanel(), gbc);
+
+ gbc.gridy++;
+ turnoverAndRetentionPanel.add(createTurnoverAndRetentionSettingsPanel(), gbc);
+
+ gbc.gridx++;
+ turnoverAndRetentionPanel.add(createTurnoverAndRetentionModifiersPanel(), gbc);
+
+ gbc.gridx++;
+ turnoverAndRetentionPanel.add(createTurnoverAndRetentionPayoutPanel(), gbc);
+
+ gbc.gridx = 0;
+ gbc.gridy++;
+ turnoverAndRetentionPanel.add(createTurnoverAndRetentionFatiguePanel(), gbc);
+
+ gbc.gridx++;
+ turnoverAndRetentionPanel.add(createTurnoverAndRetentionUnitCohesionPanel(), gbc);
+
+ final JScrollPane scrollPersonnel = new JScrollPane(turnoverAndRetentionPanel);
+ scrollPersonnel.setPreferredSize(new Dimension(500, 400));
+
+ return scrollPersonnel;
+ }
+
+ //region Life Paths Tab
private JScrollPane createLifePathsPanel() {
final AbstractMHQScrollablePanel lifePathsPanel = new DefaultMHQScrollablePanel(getFrame(),
"lifePathsPanel", new GridBagLayout());
@@ -3179,9 +3254,6 @@ private JScrollPane createLifePathsPanel() {
lifePathsPanel.add(createPersonnelRandomizationPanel(), gbc);
- gbc.gridx++;
- lifePathsPanel.add(createRetirementPanel(), gbc);
-
gbc.gridx = 0;
gbc.gridy++;
gbc.gridwidth = 2;
@@ -3337,6 +3409,85 @@ private JPanel createGeneralPersonnelPanel() {
return panel;
}
+ private void createFatigueSubPanel() {
+ lblFatigueWarning = new JLabel(resources.getString("lblFatigueWarning.text"));
+ lblFatigueWarning.setName("lblFatigueWarning");
+ lblFatigueWarning.setEnabled(campaign.getCampaignOptions().isUseFatigue());
+
+ lblFatigueRate = new JLabel(resources.getString("lblFatigueRate.text"));
+ lblFatigueRate.setToolTipText(resources.getString("lblFatigueRate.toolTipText"));
+ lblFatigueRate.setName("lblFatigueRate");
+ lblFatigueRate.setEnabled(campaign.getCampaignOptions().isUseFatigue());
+
+ spnFatigueRate = new JSpinner(new SpinnerNumberModel(1, 1, 10, 1));
+ spnFatigueRate.setToolTipText(resources.getString("lblFatigueRate.toolTipText"));
+ spnFatigueRate.setName("spnFatigueRate");
+ spnFatigueRate.setEnabled(campaign.getCampaignOptions().isUseFatigue());
+
+ chkUseInjuryFatigue = new JCheckBox(resources.getString("chkUseInjuryFatigue.text"));
+ chkUseInjuryFatigue.setToolTipText(resources.getString("chkUseInjuryFatigue.toolTipText"));
+ chkUseInjuryFatigue.setName("chkUseInjuryFatigue");
+
+ lblFieldKitchenCapacity = new JLabel(resources.getString("lblFieldKitchenCapacity.text"));
+ lblFieldKitchenCapacity.setToolTipText(resources.getString("lblFieldKitchenCapacity.toolTipText"));
+ lblFieldKitchenCapacity.setName("lblFieldKitchenCapacity");
+ lblFieldKitchenCapacity.setEnabled(campaign.getCampaignOptions().isUseFatigue());
+
+ spnFieldKitchenCapacity = new JSpinner(new SpinnerNumberModel(150, 0, 450, 1));
+ spnFieldKitchenCapacity.setToolTipText(resources.getString("lblFieldKitchenCapacity.toolTipText"));
+ spnFieldKitchenCapacity.setName("spnFieldKitchenCapacity");
+ spnFieldKitchenCapacity.setEnabled(campaign.getCampaignOptions().isUseFatigue());
+
+ lblFatigueLeaveThreshold = new JLabel(resources.getString("lblFatigueLeaveThreshold.text"));
+ lblFatigueLeaveThreshold.setToolTipText(resources.getString("lblFatigueLeaveThreshold.toolTipText"));
+ lblFatigueLeaveThreshold.setName("lblFatigueLeaveThreshold");
+ lblFatigueLeaveThreshold.setEnabled(campaign.getCampaignOptions().isUseFatigue());
+
+ spnFatigueLeaveThreshold = new JSpinner(new SpinnerNumberModel(13, 0, 17, 1));
+ spnFatigueLeaveThreshold.setToolTipText(resources.getString("lblFatigueLeaveThreshold.toolTipText"));
+ spnFatigueLeaveThreshold.setName("spnFatigueLeaveThreshold");
+ spnFatigueLeaveThreshold.setEnabled(campaign.getCampaignOptions().isUseFatigue());
+
+ fatigueSubPanel.setBorder(BorderFactory.createTitledBorder(""));
+ fatigueSubPanel.setName("fatigueSubPanel");
+ fatigueSubPanel.setEnabled(campaign.getCampaignOptions().isUseFatigue());
+
+ final GroupLayout layout = new GroupLayout(fatigueSubPanel);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+ fatigueSubPanel.setLayout(layout);
+
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addComponent(lblFatigueWarning)
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblFatigueRate)
+ .addComponent(spnFatigueRate, Alignment.LEADING))
+ .addComponent(chkUseInjuryFatigue)
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblFieldKitchenCapacity)
+ .addComponent(spnFieldKitchenCapacity, Alignment.LEADING))
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblFatigueLeaveThreshold)
+ .addComponent(spnFatigueLeaveThreshold, Alignment.LEADING))
+ );
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(lblFatigueWarning)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblFatigueRate)
+ .addComponent(spnFatigueRate))
+ .addComponent(chkUseInjuryFatigue)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblFieldKitchenCapacity)
+ .addComponent(spnFieldKitchenCapacity))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblFatigueLeaveThreshold)
+ .addComponent(spnFatigueLeaveThreshold))
+ );
+ }
+
private JPanel createExpandedPersonnelInformationPanel() {
// Initialize Labels Used in ActionListeners
final JLabel lblTimeInServiceDisplayFormat = new JLabel();
@@ -3892,162 +4043,751 @@ public Component getListCellRendererComponent(final JList> list, final Object
return panel;
}
- private JPanel createPersonnelRandomizationPanel() {
- // Create Panel Components
- chkUseDylansRandomXP = new JCheckBox(resources.getString("chkUseDylansRandomXP.text"));
- chkUseDylansRandomXP.setToolTipText(resources.getString("chkUseDylansRandomXP.toolTipText"));
- chkUseDylansRandomXP.setName("chkUseDylansRandomXP");
-
- randomOriginOptionsPanel = new RandomOriginOptionsPanel(getFrame(), campaign, comboFaction);
-
- // Layout the Panel
- final JPanel panel = new JPanel();
- panel.setBorder(BorderFactory.createTitledBorder(resources.getString("personnelRandomizationPanel.title")));
- panel.setName("personnelRandomizationPanel");
+ private JPanel createTurnoverAndRetentionHeaderPanel() {
+ chkUseRetirementDateTracking = new JCheckBox(resources.getString("chkUseRetirementDateTracking.text"));
+ chkUseRetirementDateTracking.setToolTipText(resources.getString("chkUseRetirementDateTracking.toolTipText"));
+ chkUseRetirementDateTracking.setName("chkUseRetirementDateTracking");
- final GroupLayout layout = new GroupLayout(panel);
- layout.setAutoCreateGaps(true);
- layout.setAutoCreateContainerGaps(true);
- panel.setLayout(layout);
+ chkUseRandomRetirement = new JCheckBox(resources.getString("chkUseRandomRetirement.text"));
+ chkUseRandomRetirement.setToolTipText(resources.getString("chkUseRandomRetirement.toolTipText"));
+ chkUseRandomRetirement.setName("chkUseRandomRetirement");
+ chkUseRandomRetirement.addActionListener(evt -> {
+ boolean isEnabled = chkUseRandomRetirement.isSelected();
- layout.setVerticalGroup(
- layout.createSequentialGroup()
- .addComponent(chkUseDylansRandomXP)
- .addComponent(randomOriginOptionsPanel)
- );
+ // General Handlers
+ for (Component component : turnoverAndRetentionSettingsPanel.getComponents()) {
+ component.setEnabled(isEnabled);
+ }
- layout.setHorizontalGroup(
- layout.createParallelGroup(Alignment.LEADING)
- .addComponent(chkUseDylansRandomXP)
- .addComponent(randomOriginOptionsPanel)
- );
+ for (Component component : turnoverAndRetentionModifiersPanel.getComponents()) {
+ component.setEnabled(isEnabled);
+ }
- return panel;
- }
+ for (Component component : turnoverAndRetentionPayoutPanel.getComponents()) {
+ component.setEnabled(isEnabled);
+ }
- private JPanel createRetirementPanel() {
- // Create Panel Components
- chkUseRetirementDateTracking = new JCheckBox(resources.getString("chkUseRetirementDateTracking.text"));
- chkUseRetirementDateTracking.setToolTipText(resources.getString("chkUseRetirementDateTracking.toolTipText"));
- chkUseRetirementDateTracking.setName("chkUseRetirementDateTracking");
+ for (Component component : turnoverAndRetentionUnitCohesionPanel.getComponents()) {
+ component.setEnabled(isEnabled);
+ }
- createRandomRetirementPanel();
+ // Border Handlers
+ turnoverAndRetentionSettingsPanel.setEnabled(isEnabled);
+ turnoverAndRetentionModifiersPanel.setEnabled(isEnabled);
+ turnoverAndRetentionPayoutPanel.setEnabled(isEnabled);
+ turnoverAndRetentionUnitCohesionPanel.setEnabled(isEnabled);
+
+ // Special Case Handlers
+ // These are elements whose status is influenced by other campaign options.
+ final TurnoverTargetNumberMethod turnoverTargetNumberMethod = comboTurnoverTargetNumberMethod.getSelectedItem();
+ lblTurnoverDifficulty.setEnabled((isEnabled) && (!Objects.requireNonNull(turnoverTargetNumberMethod).isFixed()));
+ comboTurnoverDifficulty.setEnabled((isEnabled) && (!Objects.requireNonNull(turnoverTargetNumberMethod).isFixed()));
+ lblTurnoverFixedTargetNumber.setEnabled((isEnabled) && (Objects.requireNonNull(turnoverTargetNumberMethod).isFixed()));
+ spnTurnoverFixedTargetNumber.setEnabled((isEnabled) && (Objects.requireNonNull(turnoverTargetNumberMethod).isFixed()));
+
+ boolean isUseLoyaltyModifiers = chkUseLoyaltyModifiers.isSelected();
+ loyaltySubPanel.setEnabled((isEnabled) && (isUseLoyaltyModifiers));
+ chkUseHideLoyalty.setEnabled((isEnabled) && (isUseLoyaltyModifiers));
+
+ boolean isUseServiceBonus = chkUsePayoutServiceBonus.isSelected();
+ payoutServiceBonusSubPanel.setEnabled((isEnabled) && (isUseServiceBonus));
+ lblPayoutServiceBonusRate.setEnabled((isEnabled) && (isUseServiceBonus));
+ spnPayoutServiceBonusRate.setEnabled((isEnabled) && (isUseServiceBonus));
+
+ boolean isUseAdministrativeStrain = chkUseAdministrativeStrain.isSelected();
+ administrativeStrainSubPanel.setEnabled((isEnabled) && (isUseAdministrativeStrain));
+ lblAdministrativeCapacity.setEnabled((isEnabled) && (isUseAdministrativeStrain));
+ spnAdministrativeCapacity.setEnabled((isEnabled) && (isUseAdministrativeStrain));
+ lblMultiCrewStrainDivider.setEnabled((isEnabled) && (isUseAdministrativeStrain));
+ spnMultiCrewStrainDivider.setEnabled((isEnabled) && (isUseAdministrativeStrain));
+
+ boolean isUseManagementSkill = chkUseManagementSkill.isSelected();
+ managementSkillSubPanel.setEnabled((isEnabled) && (isUseManagementSkill));
+ chkUseCommanderLeadershipOnly.setEnabled((isEnabled) && (isUseManagementSkill));
+ lblManagementSkillPenalty.setEnabled((isEnabled) && (isUseManagementSkill));
+ spnManagementSkillPenalty.setEnabled((isEnabled) && (isUseManagementSkill));
+ });
- // Layout the Panel
- final JPanel panel = new JPanel();
- panel.setBorder(BorderFactory.createTitledBorder(resources.getString("retirementPanel.title")));
- panel.setToolTipText(resources.getString("retirementPanel.toolTipText"));
- panel.setName("retirementPanel");
+ final JPanel turnoverAndRetentionHeaderPanel = new JPanel();
+ turnoverAndRetentionHeaderPanel.setName("turnoverAndRetentionHeaderPanel");
- final GroupLayout layout = new GroupLayout(panel);
- panel.setLayout(layout);
+ final GroupLayout layout = new GroupLayout(turnoverAndRetentionHeaderPanel);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
+ turnoverAndRetentionHeaderPanel.setLayout(layout);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addComponent(chkUseRetirementDateTracking)
- .addComponent(randomRetirementPanel)
+ .addComponent(chkUseRandomRetirement)
);
layout.setHorizontalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addComponent(chkUseRetirementDateTracking)
- .addComponent(randomRetirementPanel)
+ .addComponent(chkUseRandomRetirement)
);
- return panel;
+ return turnoverAndRetentionHeaderPanel;
}
- private void createRandomRetirementPanel() {
- // Create Panel Components
- final JLabel lblRandomRetirementMethod = new JLabel(resources.getString("lblRandomRetirementMethod.text"));
- lblRandomRetirementMethod.setToolTipText(resources.getString("lblRandomRetirementMethod.toolTipText"));
- lblRandomRetirementMethod.setName("lblRandomRetirementMethod");
+ private JPanel createTurnoverAndRetentionSettingsPanel() {
+ boolean isUseTurnover = campaign.getCampaignOptions().isUseRandomRetirement();
+ boolean isUseFixedTargetNumber = campaign.getCampaignOptions().getTurnoverTargetNumberMethod().isFixed();
- comboRandomRetirementMethod = new MMComboBox<>("comboRandomRetirementMethod", RandomRetirementMethod.values());
- comboRandomRetirementMethod.setToolTipText(resources.getString("lblRandomRetirementMethod.toolTipText"));
- comboRandomRetirementMethod.setRenderer(new DefaultListCellRenderer() {
+ lblTurnoverTargetNumberMethod = new JLabel(resources.getString("lblTurnoverTargetNumberMethod.text"));
+ lblTurnoverTargetNumberMethod.setToolTipText(resources.getString("lblTurnoverTargetNumberMethod.toolTipText"));
+ lblTurnoverTargetNumberMethod.setName("lblTurnoverTargetNumberMethod");
+ lblTurnoverTargetNumberMethod.setEnabled(isUseTurnover);
+
+ comboTurnoverTargetNumberMethod = new MMComboBox<>("comboTurnoverTargetNumberMethod", TurnoverTargetNumberMethod.values());
+ comboTurnoverTargetNumberMethod.setToolTipText(resources.getString("lblTurnoverTargetNumberMethod.toolTipText"));
+ comboTurnoverTargetNumberMethod.setEnabled(isUseTurnover);
+ comboTurnoverTargetNumberMethod.setRenderer(new DefaultListCellRenderer() {
@Override
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 RandomRetirementMethod) {
- list.setToolTipText(((RandomRetirementMethod) value).getToolTipText());
+ if (value instanceof TurnoverTargetNumberMethod) {
+ list.setToolTipText(((TurnoverTargetNumberMethod) value).getToolTipText());
}
return this;
}
});
- comboRandomRetirementMethod.addActionListener(evt -> {
- final RandomRetirementMethod method = comboRandomRetirementMethod.getSelectedItem();
+ comboTurnoverTargetNumberMethod.addActionListener(evt -> {
+ final TurnoverTargetNumberMethod method = comboTurnoverTargetNumberMethod.getSelectedItem();
+
if (method == null) {
return;
}
- final boolean enabled = randomRetirementPanel.isEnabled() && !method.isNone();
- chkUseYearEndRandomRetirement.setEnabled(enabled);
- chkUseContractCompletionRandomRetirement.setEnabled(enabled);
- chkUseCustomRetirementModifiers.setEnabled(enabled);
- chkUseRandomFounderRetirement.setEnabled(enabled);
- chkTrackUnitFatigue.setEnabled(enabled);
+
+ lblTurnoverDifficulty.setEnabled(!method.isFixed());
+ comboTurnoverDifficulty.setEnabled(!method.isFixed());
+
+ lblTurnoverFixedTargetNumber.setEnabled(method.isFixed());
+ spnTurnoverFixedTargetNumber.setEnabled(method.isFixed());
});
- chkUseYearEndRandomRetirement = new JCheckBox(resources.getString("chkUseYearEndRandomRetirement.text"));
- chkUseYearEndRandomRetirement.setToolTipText(resources.getString("chkUseYearEndRandomRetirement.toolTipText"));
- chkUseYearEndRandomRetirement.setName("chkUseYearEndRandomRetirement");
+ lblTurnoverDifficulty = new JLabel(resources.getString("lblTurnoverDifficulty.text"));
+ lblTurnoverDifficulty.setToolTipText(resources.getString("lblTurnoverDifficulty.toolTipText"));
+ lblTurnoverDifficulty.setName("lblTurnoverDifficulty");
+ lblTurnoverDifficulty.setEnabled((isUseTurnover) && (!isUseFixedTargetNumber));
+
+ final DefaultComboBoxModel skillLevelModel = new DefaultComboBoxModel<>(Skills.SKILL_LEVELS);
+ skillLevelModel.removeElement(SkillLevel.NONE);
+ comboTurnoverDifficulty = new MMComboBox<>("comboTurnoverDifficulty", skillLevelModel);
+ comboTurnoverDifficulty.setToolTipText(resources.getString("lblTurnoverDifficulty.toolTipText"));
+ comboTurnoverDifficulty.setEnabled((isUseTurnover) && (!isUseFixedTargetNumber));
+
+ lblTurnoverFixedTargetNumber = new JLabel(resources.getString("lblTurnoverFixedTargetNumber.text"));
+ lblTurnoverFixedTargetNumber.setToolTipText(resources.getString("lblTurnoverFixedTargetNumber.toolTipText"));
+ lblTurnoverFixedTargetNumber.setName("lblTurnoverFixedTargetNumber");
+ lblTurnoverFixedTargetNumber.setEnabled((isUseTurnover) && (isUseFixedTargetNumber));
+
+ spnTurnoverFixedTargetNumber = new JSpinner(new SpinnerNumberModel(3, 0, 10, 1));
+ spnTurnoverFixedTargetNumber.setToolTipText(resources.getString("lblTurnoverFixedTargetNumber.toolTipText"));
+ spnTurnoverFixedTargetNumber.setName("spnTurnoverFixedTargetNumber");
+ spnTurnoverFixedTargetNumber.setEnabled((isUseTurnover) && (isUseFixedTargetNumber));
+
+ lblTurnoverFixedTargetNumber = new JLabel(resources.getString("lblTurnoverFixedTargetNumber.text"));
+ lblTurnoverFixedTargetNumber.setToolTipText(resources.getString("lblTurnoverFixedTargetNumber.toolTipText"));
+ lblTurnoverFixedTargetNumber.setName("lblTurnoverFixedTargetNumber");
+ lblTurnoverFixedTargetNumber.setEnabled(isUseTurnover);
+
+ lblTurnoverFrequency = new JLabel(resources.getString("lblTurnoverFrequency.text"));
+ lblTurnoverFrequency.setToolTipText(resources.getString("lblTurnoverFrequency.toolTipText"));
+ lblTurnoverFrequency.setName("lblTurnoverFrequency");
+ lblTurnoverFrequency.setEnabled(isUseTurnover);
+
+ comboTurnoverFrequency = new MMComboBox<>("comboTurnoverFrequency", TurnoverFrequency.values());
+ comboTurnoverFrequency.setToolTipText(resources.getString("lblTurnoverFrequency.toolTipText"));
+ comboTurnoverFrequency.setEnabled(isUseTurnover);
+ comboTurnoverFrequency.setRenderer(new DefaultListCellRenderer() {
+ @Override
+ 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 TurnoverFrequency) {
+ list.setToolTipText(((TurnoverFrequency) value).getToolTipText());
+ }
+ return this;
+ }
+ });
chkUseContractCompletionRandomRetirement = new JCheckBox(resources.getString("chkUseContractCompletionRandomRetirement.text"));
chkUseContractCompletionRandomRetirement.setToolTipText(resources.getString("chkUseContractCompletionRandomRetirement.toolTipText"));
chkUseContractCompletionRandomRetirement.setName("chkUseContractCompletionRandomRetirement");
+ chkUseContractCompletionRandomRetirement.setEnabled(isUseTurnover);
+
+ chkUseRandomFounderTurnover = new JCheckBox(resources.getString("chkUseRandomFounderTurnover.text"));
+ chkUseRandomFounderTurnover.setToolTipText(resources.getString("chkUseRandomFounderTurnover.toolTipText"));
+ chkUseRandomFounderTurnover.setName("chkUseRandomFounderTurnover");
+ chkUseRandomFounderTurnover.setEnabled(isUseTurnover);
+
+ chkUseFounderRetirement = new JCheckBox(resources.getString("chkUseFounderRetirement.text"));
+ chkUseFounderRetirement.setToolTipText(resources.getString("chkUseFounderRetirement.toolTipText"));
+ chkUseFounderRetirement.setName("chkUseFounderRetirement");
+ chkUseFounderRetirement.setEnabled(isUseTurnover);
+
+ chkTrackOriginalUnit = new JCheckBox(resources.getString("chkTrackOriginalUnit.text"));
+ chkTrackOriginalUnit.setToolTipText(resources.getString("chkTrackOriginalUnit.toolTipText"));
+ chkTrackOriginalUnit.setName("chkTrackOriginalUnit");
+ chkTrackOriginalUnit.setEnabled(isUseTurnover);
+
+ chkAeroRecruitsHaveUnits = new JCheckBox(resources.getString("chkAeroRecruitsHaveUnits.text"));
+ chkAeroRecruitsHaveUnits.setToolTipText(resources.getString("chkAeroRecruitsHaveUnits.toolTipText"));
+ chkAeroRecruitsHaveUnits.setName("chkAeroRecruitsHaveUnits");
+ chkAeroRecruitsHaveUnits.setEnabled(isUseTurnover);
+
+ chkUseSubContractSoldiers = new JCheckBox(resources.getString("chkUseSubContractSoldiers.text"));
+ chkUseSubContractSoldiers.setToolTipText(resources.getString("chkUseSubContractSoldiers.toolTipText"));
+ chkUseSubContractSoldiers.setName("chkUseSubContractSoldiers");
+ chkUseSubContractSoldiers.setEnabled(isUseTurnover);
+
+ lblServiceContractDuration = new JLabel(resources.getString("lblServiceContractDuration.text"));
+ lblServiceContractDuration.setToolTipText(resources.getString("lblServiceContractDuration.toolTipText"));
+ lblServiceContractDuration.setName("lblServiceContractDuration");
+ lblServiceContractDuration.setEnabled(isUseTurnover);
+
+ spnServiceContractDuration = new JSpinner(new SpinnerNumberModel(36, 0, 120, 1));
+ spnServiceContractDuration.setToolTipText(resources.getString("lblServiceContractDuration.toolTipText"));
+ spnServiceContractDuration.setName("spnServiceContractDuration");
+ spnServiceContractDuration.setEnabled(isUseTurnover);
+
+ lblServiceContractModifier = new JLabel(resources.getString("lblServiceContractModifier.text"));
+ lblServiceContractModifier.setToolTipText(resources.getString("lblServiceContractModifier.toolTipText"));
+ lblServiceContractModifier.setName("lblServiceContractModifier");
+ lblServiceContractModifier.setEnabled(isUseTurnover);
+
+ spnServiceContractModifier = new JSpinner(new SpinnerNumberModel(5, 0, 10, 1));
+ spnServiceContractModifier.setToolTipText(resources.getString("lblServiceContractModifier.toolTipText"));
+ spnServiceContractModifier.setName("spnServiceContractModifier");
+ spnServiceContractModifier.setEnabled(isUseTurnover);
+
+ chkPayBonusDefault = new JCheckBox(resources.getString("chkPayBonusDefault.text"));
+ chkPayBonusDefault.setToolTipText(resources.getString("chkPayBonusDefault.toolTipText"));
+ chkPayBonusDefault.setName("chkPayBonusDefault");
+ chkPayBonusDefault.setEnabled(isUseTurnover);
+
+ turnoverAndRetentionSettingsPanel.setBorder(BorderFactory.createTitledBorder(resources.getString("turnoverAndRetentionSettingsPanel.title")));
+ turnoverAndRetentionSettingsPanel.setName("turnoverAndRetentionSettingsPanel");
+ turnoverAndRetentionSettingsPanel.setEnabled(isUseTurnover);
+
+ final GroupLayout layout = new GroupLayout(turnoverAndRetentionSettingsPanel);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+ turnoverAndRetentionSettingsPanel.setLayout(layout);
+
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblTurnoverTargetNumberMethod)
+ .addComponent(comboTurnoverTargetNumberMethod, Alignment.LEADING))
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblTurnoverDifficulty)
+ .addComponent(comboTurnoverDifficulty, Alignment.LEADING))
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblTurnoverFixedTargetNumber)
+ .addComponent(spnTurnoverFixedTargetNumber, Alignment.LEADING))
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblTurnoverFrequency)
+ .addComponent(comboTurnoverFrequency, Alignment.LEADING))
+ .addComponent(chkUseContractCompletionRandomRetirement)
+ .addComponent(chkUseRandomFounderTurnover)
+ .addComponent(chkUseFounderRetirement)
+ .addComponent(chkTrackOriginalUnit)
+ .addComponent(chkAeroRecruitsHaveUnits)
+ .addComponent(chkUseSubContractSoldiers)
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblServiceContractDuration)
+ .addComponent(spnServiceContractDuration, Alignment.LEADING))
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblServiceContractModifier)
+ .addComponent(spnServiceContractModifier, Alignment.LEADING))
+ .addComponent(chkPayBonusDefault)
+ );
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblTurnoverTargetNumberMethod)
+ .addComponent(comboTurnoverTargetNumberMethod))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblTurnoverDifficulty)
+ .addComponent(comboTurnoverDifficulty))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblTurnoverFixedTargetNumber)
+ .addComponent(spnTurnoverFixedTargetNumber))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblTurnoverFrequency)
+ .addComponent(comboTurnoverFrequency))
+ .addComponent(chkUseContractCompletionRandomRetirement)
+ .addComponent(chkUseRandomFounderTurnover)
+ .addComponent(chkUseFounderRetirement)
+ .addComponent(chkTrackOriginalUnit)
+ .addComponent(chkAeroRecruitsHaveUnits)
+ .addComponent(chkUseSubContractSoldiers)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblServiceContractDuration)
+ .addComponent(spnServiceContractDuration))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblServiceContractModifier)
+ .addComponent(spnServiceContractModifier))
+ .addComponent(chkPayBonusDefault)
+ );
+
+ return turnoverAndRetentionSettingsPanel;
+ }
+
+ private JPanel createTurnoverAndRetentionModifiersPanel() {
+ boolean isUseTurnover = campaign.getCampaignOptions().isUseRandomRetirement();
chkUseCustomRetirementModifiers = new JCheckBox(resources.getString("chkUseCustomRetirementModifiers.text"));
chkUseCustomRetirementModifiers.setToolTipText(resources.getString("chkUseCustomRetirementModifiers.toolTipText"));
chkUseCustomRetirementModifiers.setName("chkUseCustomRetirementModifiers");
+ chkUseCustomRetirementModifiers.setEnabled(campaign.getCampaignOptions().isUseRandomRetirement());
+
+ chkUseFatigueModifiers = new JCheckBox(resources.getString("chkUseFatigueModifiers.text"));
+ chkUseFatigueModifiers.setToolTipText(resources.getString("chkUseFatigueModifiers.toolTipText"));
+ chkUseFatigueModifiers.setName("chkUseFatigueModifiers");
+ chkUseFatigueModifiers.setEnabled((isUseTurnover) && (campaign.getCampaignOptions().isUseFatigue()));
+
+ chkUseSkillModifiers = new JCheckBox(resources.getString("chkUseSkillModifiers.text"));
+ chkUseSkillModifiers.setToolTipText(resources.getString("chkUseSkillModifiers.toolTipText"));
+ chkUseSkillModifiers.setName("chkUseSkillModifiers");
+ chkUseSkillModifiers.setEnabled(isUseTurnover);
+
+ chkUseAgeModifiers = new JCheckBox(resources.getString("chkUseAgeModifiers.text"));
+ chkUseAgeModifiers.setToolTipText(resources.getString("chkUseAgeModifiers.toolTipText"));
+ chkUseAgeModifiers.setName("chkUseAgeModifiers");
+ chkUseAgeModifiers.setEnabled(isUseTurnover);
+
+ chkUseUnitRatingModifiers = new JCheckBox(resources.getString("chkUseUnitRatingModifiers.text"));
+ chkUseUnitRatingModifiers.setToolTipText(resources.getString("chkUseUnitRatingModifiers.toolTipText"));
+ chkUseUnitRatingModifiers.setName("chkUseUnitRatingModifiers");
+ chkUseUnitRatingModifiers.setEnabled(isUseTurnover);
+
+ chkUseFactionModifiers = new JCheckBox(resources.getString("chkUseFactionModifiers.text"));
+ chkUseFactionModifiers.setToolTipText(resources.getString("chkUseFactionModifiers.toolTipText"));
+ chkUseFactionModifiers.setName("chkUseFactionModifiers");
+ chkUseFactionModifiers.setEnabled(isUseTurnover);
+
+ chkUseMissionStatusModifiers = new JCheckBox(resources.getString("chkUseMissionStatusModifiers.text"));
+ chkUseMissionStatusModifiers.setToolTipText(resources.getString("chkUseMissionStatusModifiers.toolTipText"));
+ chkUseMissionStatusModifiers.setName("chkUseMissionStatusModifiers");
+ chkUseMissionStatusModifiers.setEnabled(isUseTurnover);
+
+ chkUseMarriageModifiers = new JCheckBox(resources.getString("chkUseMarriageModifiers.text"));
+ chkUseMarriageModifiers.setToolTipText(resources.getString("chkUseMarriageModifiers.toolTipText"));
+ chkUseMarriageModifiers.setName("chkUseMarriageModifiers");
+ chkUseMarriageModifiers.setEnabled(isUseTurnover);
+
+ chkUseLoyaltyModifiers = new JCheckBox(resources.getString("chkUseLoyaltyModifiers.text"));
+ chkUseLoyaltyModifiers.setToolTipText(resources.getString("chkUseLoyaltyModifiers.toolTipText"));
+ chkUseLoyaltyModifiers.setName("chkUseLoyaltyModifiers");
+ chkUseLoyaltyModifiers.setEnabled(isUseTurnover);
+ chkUseLoyaltyModifiers.addActionListener(evt -> {
+ final boolean isEnabled = chkUseLoyaltyModifiers.isSelected();
+
+ for (Component component : loyaltySubPanel.getComponents()) {
+ component.setEnabled(isEnabled);
+ }
- chkUseRandomFounderRetirement = new JCheckBox(resources.getString("chkUseRandomFounderRetirement.text"));
- chkUseRandomFounderRetirement.setToolTipText(resources.getString("chkUseRandomFounderRetirement.toolTipText"));
- chkUseRandomFounderRetirement.setName("chkUseRandomFounderRetirement");
+ loyaltySubPanel.setEnabled(isEnabled);
+ });
- chkTrackUnitFatigue = new JCheckBox(resources.getString("chkTrackUnitFatigue.text"));
- chkTrackUnitFatigue.setToolTipText(resources.getString("chkTrackUnitFatigue.toolTipText"));
- chkTrackUnitFatigue.setName("chkTrackUnitFatigue");
+ createLoyaltySubPanel(isUseTurnover);
- // Programmatically Assign Accessibility Labels
- lblRandomRetirementMethod.setLabelFor(comboRandomRetirementMethod);
+ turnoverAndRetentionModifiersPanel.setBorder(BorderFactory.createTitledBorder(resources.getString("turnoverAndRetentionModifiersPanel.title")));
+ turnoverAndRetentionModifiersPanel.setName("turnoverAndRetentionModifiersPanel");
+ turnoverAndRetentionModifiersPanel.setEnabled(isUseTurnover);
- // Layout the Panel
- randomRetirementPanel = new JDisableablePanel("randomRetirementPanel");
- randomRetirementPanel.setBorder(BorderFactory.createTitledBorder(resources.getString("randomRetirementPanel.title")));
- randomRetirementPanel.setToolTipText(resources.getString("randomRetirementPanel.toolTipText"));
+ final GroupLayout layout = new GroupLayout(turnoverAndRetentionModifiersPanel);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+ turnoverAndRetentionModifiersPanel.setLayout(layout);
- final GroupLayout layout = new GroupLayout(randomRetirementPanel);
- randomRetirementPanel.setLayout(layout);
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addComponent(chkUseCustomRetirementModifiers)
+ .addComponent(chkUseFatigueModifiers)
+ .addComponent(chkUseSkillModifiers)
+ .addComponent(chkUseAgeModifiers)
+ .addComponent(chkUseUnitRatingModifiers)
+ .addComponent(chkUseFactionModifiers)
+ .addComponent(chkUseMissionStatusModifiers)
+ .addComponent(chkUseMarriageModifiers)
+ .addGap(15)
+ .addComponent(chkUseLoyaltyModifiers)
+ .addComponent(loyaltySubPanel)
+ );
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(chkUseCustomRetirementModifiers)
+ .addComponent(chkUseFatigueModifiers)
+ .addComponent(chkUseSkillModifiers)
+ .addComponent(chkUseAgeModifiers)
+ .addComponent(chkUseUnitRatingModifiers)
+ .addComponent(chkUseFactionModifiers)
+ .addComponent(chkUseMissionStatusModifiers)
+ .addComponent(chkUseMarriageModifiers)
+ .addComponent(chkUseLoyaltyModifiers)
+ .addComponent(loyaltySubPanel)
+ );
+
+ return turnoverAndRetentionModifiersPanel;
+ }
+
+ private void createLoyaltySubPanel(boolean isUseTurnover) {
+ chkUseHideLoyalty = new JCheckBox(resources.getString("chkUseHideLoyalty.text"));
+ chkUseHideLoyalty.setToolTipText(resources.getString("chkUseHideLoyalty.toolTipText"));
+ chkUseHideLoyalty.setName("chkUseHideLoyalty");
+ chkUseHideLoyalty.setEnabled(isUseTurnover && campaign.getCampaignOptions().isUseLoyaltyModifiers());
+
+ loyaltySubPanel.setBorder(BorderFactory.createTitledBorder(""));
+ loyaltySubPanel.setName("loyaltySubPanel");
+ loyaltySubPanel.setEnabled(isUseTurnover && campaign.getCampaignOptions().isUseLoyaltyModifiers());
+
+ final GroupLayout layout = new GroupLayout(loyaltySubPanel);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
+ loyaltySubPanel.setLayout(layout);
+
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addComponent(chkUseHideLoyalty)
+ );
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(chkUseHideLoyalty)
+ );
+ }
+
+ private JPanel createTurnoverAndRetentionPayoutPanel() {
+ boolean isUseTurnover = campaign.getCampaignOptions().isUseRandomRetirement();
+
+ lblPayoutRateOfficer = new JLabel(resources.getString("lblPayoutRateOfficer.text"));
+ lblPayoutRateOfficer.setToolTipText(resources.getString("lblPayoutRateOfficer.toolTipText"));
+ lblPayoutRateOfficer.setName("lblPayoutRateOfficer");
+ lblPayoutRateOfficer.setEnabled(isUseTurnover);
+
+ spnPayoutRateOfficer = new JSpinner(new SpinnerNumberModel(3, 0, 12, 1));
+ spnPayoutRateOfficer.setToolTipText(resources.getString("lblPayoutRateOfficer.toolTipText"));
+ spnPayoutRateOfficer.setName("spnPayoutRateOfficer");
+ spnPayoutRateOfficer.setEnabled(isUseTurnover);
+
+ lblPayoutRateEnlisted = new JLabel(resources.getString("lblPayoutRateEnlisted.text"));
+ lblPayoutRateEnlisted.setToolTipText(resources.getString("lblPayoutRateEnlisted.toolTipText"));
+ lblPayoutRateEnlisted.setName("lblPayoutRateEnlisted");
+ lblPayoutRateEnlisted.setEnabled(isUseTurnover);
+
+ spnPayoutRateEnlisted = new JSpinner(new SpinnerNumberModel(3, 0, 12, 1));
+ spnPayoutRateEnlisted.setToolTipText(resources.getString("lblPayoutRateEnlisted.toolTipText"));
+ spnPayoutRateEnlisted.setName("lblPayoutRateEnlisted");
+ spnPayoutRateEnlisted.setEnabled(isUseTurnover);
+
+ lblPayoutRetirementMultiplier = new JLabel(resources.getString("lblPayoutRetirementMultiplier.text"));
+ lblPayoutRetirementMultiplier.setToolTipText(resources.getString("lblPayoutRetirementMultiplier.toolTipText"));
+ lblPayoutRetirementMultiplier.setName("lblPayoutRetirementMultiplier");
+ lblPayoutRetirementMultiplier.setEnabled(isUseTurnover);
+
+ spnPayoutRetirementMultiplier = new JSpinner(new SpinnerNumberModel(24, 1, 120, 1));
+ spnPayoutRetirementMultiplier.setToolTipText(resources.getString("lblPayoutRateEnlisted.toolTipText"));
+ spnPayoutRetirementMultiplier.setName("spnPayoutRetirementMultiplier");
+ spnPayoutRetirementMultiplier.setEnabled(isUseTurnover);
+
+ chkUsePayoutServiceBonus = new JCheckBox(resources.getString("chkUsePayoutServiceBonus.text"));
+ chkUsePayoutServiceBonus.setToolTipText(resources.getString("chkUsePayoutServiceBonus.toolTipText"));
+ chkUsePayoutServiceBonus.setName("chkUsePayoutServiceBonus");
+ chkUsePayoutServiceBonus.setEnabled(isUseTurnover);
+ chkUsePayoutServiceBonus.addActionListener(evt -> {
+ final boolean isEnabled = chkUsePayoutServiceBonus.isSelected();
+
+ for (Component component : payoutServiceBonusSubPanel.getComponents()) {
+ component.setEnabled(isEnabled);
+ }
+
+ payoutServiceBonusSubPanel.setEnabled(isEnabled);
+ });
+
+ createPayoutServiceBonusSubPanel(isUseTurnover);
+
+ turnoverAndRetentionPayoutPanel.setBorder(BorderFactory.createTitledBorder(resources.getString("turnoverAndRetentionPayoutPanel.title")));
+ turnoverAndRetentionPayoutPanel.setName("turnoverAndRetentionPayoutPanel");
+ turnoverAndRetentionPayoutPanel.setEnabled(isUseTurnover);
+
+ final GroupLayout layout = new GroupLayout(turnoverAndRetentionPayoutPanel);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+ turnoverAndRetentionPayoutPanel.setLayout(layout);
layout.setVerticalGroup(
layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(Alignment.BASELINE)
- .addComponent(lblRandomRetirementMethod)
- .addComponent(comboRandomRetirementMethod, Alignment.LEADING))
- .addComponent(chkUseYearEndRandomRetirement)
- .addComponent(chkUseContractCompletionRandomRetirement)
- .addComponent(chkUseRandomFounderRetirement)
- .addComponent(chkUseCustomRetirementModifiers)
- .addComponent(chkTrackUnitFatigue)
+ .addComponent(lblPayoutRateOfficer)
+ .addComponent(spnPayoutRateOfficer, Alignment.LEADING))
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblPayoutRateEnlisted)
+ .addComponent(spnPayoutRateEnlisted, Alignment.LEADING))
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblPayoutRetirementMultiplier)
+ .addComponent(spnPayoutRetirementMultiplier, Alignment.LEADING))
+ .addGap(15)
+ .addComponent(chkUsePayoutServiceBonus)
+ .addComponent(payoutServiceBonusSubPanel)
);
layout.setHorizontalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
- .addComponent(lblRandomRetirementMethod)
- .addComponent(comboRandomRetirementMethod))
- .addComponent(chkUseYearEndRandomRetirement)
- .addComponent(chkUseContractCompletionRandomRetirement)
- .addComponent(chkUseRandomFounderRetirement)
- .addComponent(chkUseCustomRetirementModifiers)
- .addComponent(chkTrackUnitFatigue)
+ .addComponent(lblPayoutRateOfficer)
+ .addComponent(spnPayoutRateOfficer))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblPayoutRateEnlisted)
+ .addComponent(spnPayoutRateEnlisted))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblPayoutRetirementMultiplier)
+ .addComponent(spnPayoutRetirementMultiplier))
+ .addComponent(chkUsePayoutServiceBonus)
+ .addComponent(payoutServiceBonusSubPanel)
);
+
+ return turnoverAndRetentionPayoutPanel;
+ }
+
+ private void createPayoutServiceBonusSubPanel(boolean isUseTurnover) {
+ boolean isUseServiceBonus = campaign.getCampaignOptions().isUsePayoutServiceBonus();
+
+ lblPayoutServiceBonusRate = new JLabel(resources.getString("lblPayoutServiceBonusRate.text"));
+ lblPayoutServiceBonusRate.setToolTipText(resources.getString("lblPayoutServiceBonusRate.toolTipText"));
+ lblPayoutServiceBonusRate.setName("lblPayoutServiceBonusRate");
+ lblPayoutServiceBonusRate.setEnabled((isUseTurnover) && (isUseServiceBonus));
+
+ spnPayoutServiceBonusRate = new JSpinner(new SpinnerNumberModel(10, 1, 100, 1));
+ spnPayoutServiceBonusRate.setToolTipText(resources.getString("lblPayoutServiceBonusRate.toolTipText"));
+ spnPayoutServiceBonusRate.setName("spnPayoutServiceBonusRate");
+ spnPayoutServiceBonusRate.setEnabled((isUseTurnover) && (isUseServiceBonus));
+
+ payoutServiceBonusSubPanel.setBorder(BorderFactory.createTitledBorder(""));
+ payoutServiceBonusSubPanel.setName("payoutServiceBonusSubPanel");
+ payoutServiceBonusSubPanel.setEnabled((isUseTurnover) && (isUseServiceBonus));
+
+ final GroupLayout layout = new GroupLayout(payoutServiceBonusSubPanel);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+ payoutServiceBonusSubPanel.setLayout(layout);
+
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblPayoutServiceBonusRate)
+ .addComponent(spnPayoutServiceBonusRate, Alignment.LEADING))
+ );
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblPayoutServiceBonusRate)
+ .addComponent(spnPayoutServiceBonusRate))
+ );
+ }
+
+ private JPanel createTurnoverAndRetentionUnitCohesionPanel() {
+ boolean isUseTurnover = campaign.getCampaignOptions().isUseRandomRetirement();
+
+ chkUseAdministrativeStrain = new JCheckBox(resources.getString("chkUseAdministrativeStrain.text"));
+ chkUseAdministrativeStrain.setToolTipText(resources.getString("chkUseAdministrativeStrain.toolTipText"));
+ chkUseAdministrativeStrain.setName("chkUseAdministrativeStrain");
+ chkUseAdministrativeStrain.setEnabled(isUseTurnover);
+ chkUseAdministrativeStrain.addActionListener(evt -> {
+ final boolean isEnabled = chkUseAdministrativeStrain.isSelected();
+
+ for (Component component : administrativeStrainSubPanel.getComponents()) {
+ component.setEnabled(isEnabled);
+ }
+
+ administrativeStrainSubPanel.setEnabled(isEnabled);
+ });
+
+ createAdministrativeStrainSubPanel(isUseTurnover);
+
+ chkUseManagementSkill = new JCheckBox(resources.getString("chkUseManagementSkill.text"));
+ chkUseManagementSkill.setToolTipText(resources.getString("chkUseManagementSkill.toolTipText"));
+ chkUseManagementSkill.setName("chkUseManagementSkill");
+ chkUseManagementSkill.setEnabled(isUseTurnover);
+ chkUseManagementSkill.addActionListener(evt -> {
+ final boolean isEnabled = chkUseManagementSkill.isSelected();
+
+ for (Component component : managementSkillSubPanel.getComponents()) {
+ component.setEnabled(isEnabled);
+ }
+
+ managementSkillSubPanel.setEnabled(isEnabled);
+ });
+
+ createManagementSkillSubPanel(isUseTurnover);
+
+ turnoverAndRetentionUnitCohesionPanel.setBorder(BorderFactory.createTitledBorder(resources.getString("turnoverAndRetentionUnitCohesionPanel.title")));
+ turnoverAndRetentionUnitCohesionPanel.setName("turnoverAndRetentionUnitCohesionPanel");
+ turnoverAndRetentionUnitCohesionPanel.setEnabled(isUseTurnover);
+
+ final GroupLayout layout = new GroupLayout(turnoverAndRetentionUnitCohesionPanel);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+ turnoverAndRetentionUnitCohesionPanel.setLayout(layout);
+
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addComponent(chkUseAdministrativeStrain)
+ .addComponent(administrativeStrainSubPanel)
+ .addGap(15)
+ .addComponent(chkUseManagementSkill)
+ .addComponent(managementSkillSubPanel)
+ );
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(chkUseAdministrativeStrain)
+ .addComponent(administrativeStrainSubPanel)
+ .addComponent(chkUseManagementSkill)
+ .addComponent(managementSkillSubPanel)
+ );
+
+ return turnoverAndRetentionUnitCohesionPanel;
+ }
+
+ private void createAdministrativeStrainSubPanel(boolean isUseTurnover) {
+ boolean isUseAdministrativeStrain = campaign.getCampaignOptions().isUseAdministrativeStrain();
+
+ lblAdministrativeCapacity = new JLabel(resources.getString("lblAdministrativeCapacity.text"));
+ lblAdministrativeCapacity.setToolTipText(resources.getString("lblAdministrativeCapacity.toolTipText"));
+ lblAdministrativeCapacity.setName("lblAdministrativeCapacity");
+ lblAdministrativeCapacity.setEnabled((isUseTurnover) && (isUseAdministrativeStrain));
+
+ spnAdministrativeCapacity = new JSpinner(new SpinnerNumberModel(10, 1, 30, 1));
+ spnAdministrativeCapacity.setToolTipText(resources.getString("lblAdministrativeCapacity.toolTipText"));
+ spnAdministrativeCapacity.setName("spnAdministrativeCapacity");
+ spnAdministrativeCapacity.setEnabled((isUseTurnover) && (isUseAdministrativeStrain));
+
+ lblMultiCrewStrainDivider = new JLabel(resources.getString("lblMultiCrewStrainDivider.text"));
+ lblMultiCrewStrainDivider.setToolTipText(resources.getString("lblMultiCrewStrainDivider.toolTipText"));
+ lblMultiCrewStrainDivider.setName("lblMultiCrewStrainDivider");
+ lblMultiCrewStrainDivider.setEnabled((isUseTurnover) && (isUseAdministrativeStrain));
+
+ spnMultiCrewStrainDivider = new JSpinner(new SpinnerNumberModel(5, 1, 25, 1));
+ spnMultiCrewStrainDivider.setToolTipText(resources.getString("lblMultiCrewStrainDivider.toolTipText"));
+ spnMultiCrewStrainDivider.setName("spnMultiCrewStrainDivider");
+ spnMultiCrewStrainDivider.setEnabled((isUseTurnover) && (isUseAdministrativeStrain));
+
+ administrativeStrainSubPanel.setBorder(BorderFactory.createTitledBorder(""));
+ administrativeStrainSubPanel.setName("administrativeStrainSubPanel");
+ administrativeStrainSubPanel.setEnabled((isUseTurnover) && (isUseAdministrativeStrain));
+
+ final GroupLayout layout = new GroupLayout(administrativeStrainSubPanel);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+ administrativeStrainSubPanel.setLayout(layout);
+
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblAdministrativeCapacity)
+ .addComponent(spnAdministrativeCapacity, Alignment.LEADING))
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblMultiCrewStrainDivider)
+ .addComponent(spnMultiCrewStrainDivider, Alignment.LEADING))
+ );
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblAdministrativeCapacity)
+ .addComponent(spnAdministrativeCapacity))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblMultiCrewStrainDivider)
+ .addComponent(spnMultiCrewStrainDivider))
+ );
+ }
+
+ private void createManagementSkillSubPanel(boolean isUseTurnover) {
+ boolean isUseManagementSkill = campaign.getCampaignOptions().isUseManagementSkill();
+
+ chkUseCommanderLeadershipOnly = new JCheckBox(resources.getString("chkUseCommanderLeadershipOnly.text"));
+ chkUseCommanderLeadershipOnly.setToolTipText(resources.getString("chkUseCommanderLeadershipOnly.toolTipText"));
+ chkUseCommanderLeadershipOnly.setName("chkUseCommanderLeadershipOnly");
+ chkUseCommanderLeadershipOnly.setEnabled((isUseTurnover) && (isUseManagementSkill));
+
+ lblManagementSkillPenalty = new JLabel(resources.getString("lblManagementSkillPenalty.text"));
+ lblManagementSkillPenalty.setToolTipText(resources.getString("lblManagementSkillPenalty.toolTipText"));
+ lblManagementSkillPenalty.setName("lblManagementSkillPenalty");
+ lblManagementSkillPenalty.setEnabled((isUseTurnover) && (isUseManagementSkill));
+
+ spnManagementSkillPenalty = new JSpinner(new SpinnerNumberModel(-2, -10, 0, 1));
+ spnManagementSkillPenalty.setToolTipText(resources.getString("lblManagementSkillPenalty.toolTipText"));
+ spnManagementSkillPenalty.setName("spnManagementSkillPenalty");
+ spnManagementSkillPenalty.setEnabled((isUseTurnover) && (isUseManagementSkill));
+
+ managementSkillSubPanel.setBorder(BorderFactory.createTitledBorder(""));
+ managementSkillSubPanel.setName("managementSkillSubPanel");
+ managementSkillSubPanel.setEnabled((isUseTurnover) && (isUseManagementSkill));
+
+ final GroupLayout layout = new GroupLayout(managementSkillSubPanel);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+ managementSkillSubPanel.setLayout(layout);
+
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addComponent(chkUseCommanderLeadershipOnly)
+ .addGroup(layout.createParallelGroup(Alignment.BASELINE)
+ .addComponent(lblManagementSkillPenalty)
+ .addComponent(spnManagementSkillPenalty, Alignment.LEADING))
+ );
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(chkUseCommanderLeadershipOnly)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(lblManagementSkillPenalty)
+ .addComponent(spnManagementSkillPenalty))
+ );
+ }
+
+ private JPanel createPersonnelRandomizationPanel() {
+ // Create Panel Components
+ chkUseDylansRandomXP = new JCheckBox(resources.getString("chkUseDylansRandomXP.text"));
+ chkUseDylansRandomXP.setToolTipText(resources.getString("chkUseDylansRandomXP.toolTipText"));
+ chkUseDylansRandomXP.setName("chkUseDylansRandomXP");
+
+ randomOriginOptionsPanel = new RandomOriginOptionsPanel(getFrame(), campaign, comboFaction);
+
+ // Layout the Panel
+ final JPanel panel = new JPanel();
+ panel.setBorder(BorderFactory.createTitledBorder(resources.getString("personnelRandomizationPanel.title")));
+ panel.setName("personnelRandomizationPanel");
+
+ final GroupLayout layout = new GroupLayout(panel);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+ panel.setLayout(layout);
+
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addComponent(chkUseDylansRandomXP)
+ .addComponent(randomOriginOptionsPanel)
+ );
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(chkUseDylansRandomXP)
+ .addComponent(randomOriginOptionsPanel)
+ );
+
+ return panel;
}
private JPanel createFamilyPanel() {
@@ -4118,6 +4858,47 @@ private JPanel createDependentPanel() {
return panel;
}
+ private JPanel createTurnoverAndRetentionFatiguePanel() {
+ chkUseFatigue = new JCheckBox(resources.getString("chkUseFatigue.text"));
+ chkUseFatigue.setToolTipText(resources.getString("chkUseFatigue.toolTipText"));
+ chkUseFatigue.setName("chkUseFatigue");
+ chkUseFatigue.addActionListener(evt -> {
+ final boolean isEnabled = chkUseFatigue.isSelected();
+
+ for (Component component : fatigueSubPanel.getComponents()) {
+ component.setEnabled(isEnabled);
+ }
+
+ fatigueSubPanel.setEnabled(isEnabled);
+
+ chkUseFatigueModifiers.setEnabled(isEnabled);
+ });
+
+ createFatigueSubPanel();
+
+ turnoverAndRetentionFatiguePanel.setBorder(BorderFactory.createTitledBorder(resources.getString("turnoverAndRetentionFatiguePanel.title")));
+ turnoverAndRetentionFatiguePanel.setName("turnoverAndRetentionFatiguePanel");
+
+ final GroupLayout layout = new GroupLayout(turnoverAndRetentionFatiguePanel);
+ turnoverAndRetentionFatiguePanel.setLayout(layout);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addComponent(chkUseFatigue)
+ .addComponent(fatigueSubPanel)
+ );
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(chkUseFatigue)
+ .addComponent(fatigueSubPanel)
+ );
+
+ return turnoverAndRetentionFatiguePanel;
+ }
+
private void createRandomDependentPanel() {
// Create Panel Components
final JLabel lblRandomDependentMethod = new JLabel(resources.getString("lblRandomDependentMethod.text"));
@@ -6387,6 +7168,77 @@ private JPanel createUsedPartsValueMultipliersPanel(boolean reverseQualities) {
return panel;
}
+
+ private JPanel createSharesPanel() {
+ chkUseShareSystem = new JCheckBox(resources.getString("chkUseShareSystem.text"));
+ chkUseShareSystem.setToolTipText(resources.getString("chkUseShareSystem.toolTipText"));
+ chkUseShareSystem.setName("chkUseShareSystem");
+ chkUseShareSystem.addActionListener(evt -> {
+ final boolean isEnabled = chkUseShareSystem.isSelected();
+
+ // general handlers
+ for (Component component : sharesSubPanel.getComponents()) {
+ component.setEnabled(isEnabled);
+ }
+ });
+
+ sharesSubPanel = createSharesSubPanel();
+ for (Component component : sharesSubPanel.getComponents()) {
+ component.setEnabled(campaign.getCampaignOptions().isUseShareSystem());
+ }
+
+ sharesPanel = new JDisableablePanel("sharesPanel");
+ sharesPanel.setBorder(BorderFactory.createTitledBorder(resources.getString("sharesPanel.title")));
+ sharesPanel.setEnabled(campaign.getCampaignOptions().isUseShareSystem());
+
+ final GroupLayout layout = new GroupLayout(sharesPanel);
+ sharesPanel.setLayout(layout);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+
+ layout.setVerticalGroup(
+ layout.createSequentialGroup()
+ .addComponent(chkUseShareSystem)
+ .addComponent(sharesSubPanel)
+ );
+
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(Alignment.LEADING)
+ .addComponent(chkUseShareSystem)
+ .addComponent(sharesSubPanel)
+ );
+
+ return sharesPanel;
+ }
+
+ private JPanel createSharesSubPanel() {
+ chkSharesExcludeLargeCraft = new JCheckBox(resources.getString("chkSharesExcludeLargeCraft.text"));
+ chkSharesExcludeLargeCraft.setToolTipText(resources.getString("chkSharesExcludeLargeCraft.toolTipText"));
+ chkSharesExcludeLargeCraft.setName("chkSharesExcludeLargeCraft");
+
+ chkSharesForAll = new JCheckBox(resources.getString("chkSharesForAll.text"));
+ chkSharesForAll.setToolTipText(resources.getString("chkSharesForAll.toolTipText"));
+ chkSharesForAll.setName("chkSharesForAll");
+
+ sharesSubPanel = new JDisableablePanel("sharesSubPanel");
+
+ final GroupLayout layout = new GroupLayout(sharesSubPanel);
+ sharesSubPanel.setLayout(layout);
+ layout.setAutoCreateGaps(true);
+ layout.setAutoCreateContainerGaps(true);
+
+ layout.setVerticalGroup(layout
+ .createSequentialGroup()
+ .addComponent(chkSharesExcludeLargeCraft)
+ .addComponent(chkSharesForAll));
+
+ layout.setHorizontalGroup(layout
+ .createParallelGroup(Alignment.LEADING)
+ .addComponent(chkSharesExcludeLargeCraft)
+ .addComponent(chkSharesForAll));
+
+ return sharesSubPanel;
+ }
//endregion Finances Tab
//region Rank Systems Tab
@@ -7092,20 +7944,68 @@ public void setOptions(@Nullable CampaignOptions options,
chkEnableMiscAwards.setSelected(options.isEnableMiscAwards());
//endregion Personnel Tab
+ //region Turnover and Retention Tab
+ // Header
+ chkUseRetirementDateTracking.setSelected(options.isUseRetirementDateTracking());
+ chkUseRandomRetirement.setSelected(options.isUseRandomRetirement());
+
+ // Settings
+ comboTurnoverTargetNumberMethod.setSelectedItem(options.getTurnoverTargetNumberMethod());
+ comboTurnoverDifficulty.setSelectedItem(options.getTurnoverDifficulty());
+ spnTurnoverFixedTargetNumber.setValue(options.getTurnoverFixedTargetNumber());
+ comboTurnoverFrequency.setSelectedItem(options.getTurnoverFrequency());
+ chkUseContractCompletionRandomRetirement.setSelected(options.isUseContractCompletionRandomRetirement());
+ chkUseRandomFounderTurnover.setSelected(options.isUseRandomFounderTurnover());
+ chkUseFounderRetirement.setSelected(options.isUseFounderRetirement());
+ chkTrackOriginalUnit.setSelected(options.isTrackOriginalUnit());
+ chkAeroRecruitsHaveUnits.setSelected(options.isAeroRecruitsHaveUnits());
+ chkUseSubContractSoldiers.setSelected(options.isUseSubContractSoldiers());
+ spnServiceContractDuration.setValue(options.getServiceContractDuration());
+ spnServiceContractModifier.setValue(options.getServiceContractModifier());
+ chkPayBonusDefault.setSelected(options.isPayBonusDefault());
+
+ // Modifiers
+ chkUseCustomRetirementModifiers.setSelected(options.isUseCustomRetirementModifiers());
+ chkUseFatigueModifiers.setSelected(options.isUseFatigueModifiers());
+ chkUseSkillModifiers.setSelected(options.isUseSkillModifiers());
+ chkUseAgeModifiers.setSelected(options.isUseAgeModifiers());
+ chkUseUnitRatingModifiers.setSelected(options.isUseUnitRatingModifiers());
+ chkUseFactionModifiers.setSelected(options.isUseFactionModifiers());
+ chkUseMissionStatusModifiers.setSelected(options.isUseMissionStatusModifiers());
+ chkUseMarriageModifiers.setSelected(options.isUseMarriageModifiers());
+ chkUseLoyaltyModifiers.setSelected(options.isUseLoyaltyModifiers());
+ chkUseHideLoyalty.setSelected(options.isUseHideLoyalty());
+
+ // Payouts
+ spnPayoutRateOfficer.setValue(options.getPayoutRateOfficer());
+ spnPayoutRateEnlisted.setValue(options.getPayoutRateEnlisted());
+ spnPayoutRetirementMultiplier.setValue(options.getPayoutRetirementMultiplier());
+ chkUsePayoutServiceBonus.setSelected(options.isUsePayoutServiceBonus());
+ spnPayoutServiceBonusRate.setValue(options.getPayoutServiceBonusRate());
+
+ // Unit Cohesion
+ chkUseAdministrativeStrain.setSelected(options.isUseAdministrativeStrain());
+ chkUseManagementSkill.setSelected(options.isUseManagementSkill());
+
+ spnAdministrativeCapacity.setValue(options.getAdministrativeCapacity());
+ spnMultiCrewStrainDivider.setValue(options.getMultiCrewStrainDivider());
+
+ chkUseCommanderLeadershipOnly.setSelected(options.isUseCommanderLeadershipOnly());
+ spnManagementSkillPenalty.setValue(options.getManagementSkillPenalty());
+
+ // Fatigue
+ chkUseFatigue.setSelected(options.isUseFatigue());
+ spnFatigueRate.setValue(options.getFatigueRate());
+ chkUseInjuryFatigue.setVisible(options.isUseInjuryFatigue());
+ spnFieldKitchenCapacity.setValue(options.getFieldKitchenCapacity());
+ spnFatigueLeaveThreshold.setValue(options.getFatigueLeaveThreshold());
+ //endregion Turnover and Retention Tab
+
//region Life Paths Tab
// Personnel Randomization
chkUseDylansRandomXP.setSelected(options.isUseDylansRandomXP());
randomOriginOptionsPanel.setOptions(options.getRandomOriginOptions());
- // Retirement
- chkUseRetirementDateTracking.setSelected(options.isUseRetirementDateTracking());
- comboRandomRetirementMethod.setSelectedItem(options.getRandomRetirementMethod());
- chkUseYearEndRandomRetirement.setSelected(options.isUseYearEndRandomRetirement());
- chkUseContractCompletionRandomRetirement.setSelected(options.isUseContractCompletionRandomRetirement());
- chkUseCustomRetirementModifiers.setSelected(options.isUseCustomRetirementModifiers());
- chkUseRandomFounderRetirement.setSelected(options.isUseRandomFounderRetirement());
- chkTrackUnitFatigue.setSelected(options.isTrackUnitFatigue());
-
// Family
comboFamilyDisplayLevel.setSelectedItem(options.getFamilyDisplayLevel());
@@ -7267,6 +8167,11 @@ public void setOptions(@Nullable CampaignOptions options,
spnDamagedPartsValueMultiplier.setValue(options.getDamagedPartsValueMultiplier());
spnUnrepairablePartsValueMultiplier.setValue(options.getUnrepairablePartsValueMultiplier());
spnCancelledOrderRefundMultiplier.setValue(options.getCancelledOrderRefundMultiplier());
+
+ // Shares
+ chkUseShareSystem.setSelected(options.isUseShareSystem());
+ chkSharesExcludeLargeCraft.setSelected(options.isSharesExcludeLargeCraft());
+ chkSharesForAll.setSelected(options.isSharesForAll());
//endregion Finances Tab
//region Mercenary Tab
@@ -7418,12 +8323,6 @@ public void setOptions(@Nullable CampaignOptions options,
}
chkUseStratCon.setSelected(options.isUseStratCon());
comboSkillLevel.setSelectedItem(options.getSkillLevel());
- chkUseShareSystem.setSelected(options.isUseShareSystem());
- chkSharesExcludeLargeCraft.setSelected(options.isSharesExcludeLargeCraft());
- chkSharesForAll.setSelected(options.isSharesForAll());
- chkAeroRecruitsHaveUnits.setSelected(options.isAeroRecruitsHaveUnits());
- chkUseLeadership.setSelected(options.isUseLeadership());
- chkTrackOriginalUnit.setSelected(options.isTrackOriginalUnit());
chkUseAero.setSelected(options.isUseAero());
chkUseVehicles.setSelected(options.isUseVehicles());
chkClanVehicles.setSelected(options.isClanVehicles());
@@ -7721,20 +8620,67 @@ public void updateOptions() {
options.setEnableMiscAwards(chkEnableMiscAwards.isSelected());
//endregion Personnel Tab
+ //region Turnover and Retention
+ // Header
+ options.setUseRetirementDateTracking(chkUseRetirementDateTracking.isSelected());
+ options.setUseRandomRetirement(chkUseRandomRetirement.isSelected());
+
+ // Settings
+ options.setTurnoverTargetNumberMethod(comboTurnoverTargetNumberMethod.getSelectedItem());
+ options.setTurnoverDifficulty(comboTurnoverDifficulty.getSelectedItem());
+ options.setTurnoverFixedTargetNumber((Integer) spnTurnoverFixedTargetNumber.getValue());
+ options.setTurnoverFrequency(comboTurnoverFrequency.getSelectedItem());
+ options.setUseContractCompletionRandomRetirement(chkUseContractCompletionRandomRetirement.isSelected());
+ options.setUseRandomFounderTurnover(chkUseRandomFounderTurnover.isSelected());
+ options.setUseFounderRetirement(chkUseFounderRetirement.isSelected());
+ options.setAeroRecruitsHaveUnits(chkAeroRecruitsHaveUnits.isSelected());
+ options.setUseSubContractSoldiers(chkUseSubContractSoldiers.isSelected());
+ options.setServiceContractDuration((Integer) spnServiceContractDuration.getValue());
+ options.setServiceContractModifier((Integer) spnServiceContractModifier.getValue());
+ options.setPayBonusDefault(chkPayBonusDefault.isSelected());
+
+ // Modifiers
+ options.setUseCustomRetirementModifiers(chkUseCustomRetirementModifiers.isSelected());
+ options.setUseFatigueModifiers(chkUseFatigueModifiers.isSelected());
+ options.setUseSkillModifiers(chkUseSkillModifiers.isSelected());
+ options.setUseAgeModifiers(chkUseAgeModifiers.isSelected());
+ options.setUseUnitRatingModifiers(chkUseUnitRatingModifiers.isSelected());
+ options.setUseFactionModifiers(chkUseFactionModifiers.isSelected());
+ options.setUseMissionStatusModifiers(chkUseMissionStatusModifiers.isSelected());
+ options.setUseMarriageModifiers(chkUseMarriageModifiers.isSelected());
+ options.setUseLoyaltyModifiers(chkUseLoyaltyModifiers.isSelected());
+ options.setUseHideLoyalty(chkUseHideLoyalty.isSelected());
+
+ // Payouts
+ options.setPayoutRateOfficer((Integer) spnPayoutRateOfficer.getValue());
+ options.setPayoutRateEnlisted((Integer) spnPayoutRateEnlisted.getValue());
+ options.setPayoutRetirementMultiplier((Integer) spnPayoutRetirementMultiplier.getValue());
+ options.setUsePayoutServiceBonus(chkUsePayoutServiceBonus.isSelected());
+ options.setPayoutServiceBonusRate((Integer) spnPayoutServiceBonusRate.getValue());
+
+ // Unit Cohesion
+ options.setUseAdministrativeStrain(chkUseAdministrativeStrain.isSelected());
+ options.setUseManagementSkill(chkUseManagementSkill.isSelected());
+
+ options.setAdministrativeCapacity((Integer) spnAdministrativeCapacity.getValue());
+ options.setMultiCrewStrainDivider((Integer) spnMultiCrewStrainDivider.getValue());
+
+ options.setUseCommanderLeadershipOnly(chkUseCommanderLeadershipOnly.isSelected());
+ options.setManagementSkillPenalty((Integer) spnManagementSkillPenalty.getValue());
+
+ // Fatigue
+ options.setUseFatigue(chkUseFatigue.isSelected());
+ options.setFatigueRate((Integer) spnFatigueRate.getValue());
+ options.setUseInjuryFatigue(chkUseInjuryFatigue.isSelected());
+ options.setFieldKitchenCapacity((Integer) spnFieldKitchenCapacity.getValue());
+ options.setFatigueLeaveThreshold((Integer) spnFatigueLeaveThreshold.getValue());
+ //endregion Turnover and Retention
+
//region Life Paths Tab
// Personnel Randomization
options.setUseDylansRandomXP(chkUseDylansRandomXP.isSelected());
options.setRandomOriginOptions(randomOriginOptionsPanel.createOptionsFromPanel());
- // Retirement
- options.setUseRetirementDateTracking(chkUseRetirementDateTracking.isSelected());
- options.setRandomRetirementMethod(comboRandomRetirementMethod.getSelectedItem());
- options.setUseYearEndRandomRetirement(chkUseYearEndRandomRetirement.isSelected());
- options.setUseContractCompletionRandomRetirement(chkUseContractCompletionRandomRetirement.isSelected());
- options.setUseCustomRetirementModifiers(chkUseCustomRetirementModifiers.isSelected());
- options.setUseRandomFounderRetirement(chkUseRandomFounderRetirement.isSelected());
- options.setTrackUnitFatigue(chkTrackUnitFatigue.isSelected());
-
// Family
options.setFamilyDisplayLevel(comboFamilyDisplayLevel.getSelectedItem());
@@ -7836,9 +8782,6 @@ public void updateOptions() {
options.getAgeRangeRandomDeathMaleValues().put(ageRange, (double) spnAgeRangeRandomDeathMaleValues.get(ageRange).getValue());
options.getAgeRangeRandomDeathFemaleValues().put(ageRange, (double) spnAgeRangeRandomDeathFemaleValues.get(ageRange).getValue());
}
-
- // Education
- // TODO add education options
//endregion Life Paths Tab
//region Finances Tab
@@ -7855,6 +8798,10 @@ public void updateOptions() {
options.setDamagedPartsValueMultiplier((Double) spnDamagedPartsValueMultiplier.getValue());
options.setUnrepairablePartsValueMultiplier((Double) spnUnrepairablePartsValueMultiplier.getValue());
options.setCancelledOrderRefundMultiplier((Double) spnCancelledOrderRefundMultiplier.getValue());
+
+ options.setUseShareSystem(chkUseShareSystem.isSelected());
+ options.setSharesExcludeLargeCraft(chkSharesExcludeLargeCraft.isSelected());
+ options.setSharesForAll(chkSharesForAll.isSelected());
//endregion Finances Tab
//start SPA
@@ -7897,14 +8844,8 @@ public void updateOptions() {
options.setUseAtB(chkUseAtB.isSelected());
options.setUseStratCon(chkUseStratCon.isSelected());
options.setSkillLevel(comboSkillLevel.getSelectedItem());
- options.setUseShareSystem(chkUseShareSystem.isSelected());
- options.setSharesExcludeLargeCraft(chkSharesExcludeLargeCraft.isSelected());
- options.setSharesForAll(chkSharesForAll.isSelected());
- options.setTrackOriginalUnit(chkTrackOriginalUnit.isSelected());
- options.setTrackUnitFatigue(chkTrackUnitFatigue.isSelected());
options.setLimitLanceWeight(chkLimitLanceWeight.isSelected());
options.setLimitLanceNumUnits(chkLimitLanceNumUnits.isSelected());
- options.setUseLeadership(chkUseLeadership.isSelected());
options.setUseStrategy(chkUseStrategy.isSelected());
options.setBaseStrategyDeployment((Integer) spnBaseStrategyDeployment.getValue());
options.setAdditionalStrategyDeployment((Integer) spnAdditionalStrategyDeployment.getValue());
@@ -7939,7 +8880,6 @@ public void updateOptions() {
options.setUseWeatherConditions(chkUseWeatherConditions.isSelected());
options.setUseLightConditions(chkUseLightConditions.isSelected());
options.setUsePlanetaryConditions(chkUsePlanetaryConditions.isSelected());
- options.setAeroRecruitsHaveUnits(chkAeroRecruitsHaveUnits.isSelected());
//endregion Against the Bot
campaign.setCampaignOptions(options);
diff --git a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java
index 1213d3855e..e3510924ba 100644
--- a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java
+++ b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java
@@ -1240,6 +1240,12 @@ private JPanel fillSkills() {
JLabel lblEdgeAvail1 = new JLabel();
JLabel lblEdgeAvail2 = new JLabel();
+ JLabel lblLoyalty1 = new JLabel();
+ JLabel lblLoyalty2 = new JLabel();
+
+ JLabel lblFatigue1 = new JLabel();
+ JLabel lblFatigue2 = new JLabel();
+
// education
JLabel lblEducationLevel1 = new JLabel();
JLabel lblEducationLevel2 = new JLabel();
@@ -1425,6 +1431,75 @@ private JPanel fillSkills() {
firsty++;
}
+ if ((campaign.getCampaignOptions().isUseLoyaltyModifiers())
+ && (!campaign.getCampaignOptions().isUseHideLoyalty())
+ && (person.getLoyalty() != 0)) {
+ lblLoyalty1.setName("lblLoyalty1");
+ lblLoyalty1.setText(resourceMap.getString("lblLoyalty1.text"));
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = firsty;
+ gridBagConstraints.fill = GridBagConstraints.NONE;
+ gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+ pnlSkills.add(lblLoyalty1, gridBagConstraints);
+
+ lblLoyalty2.setName("lblLoyalty2");
+ lblLoyalty2.setText(String.valueOf(person.getLoyalty()));
+ lblLoyalty2.setLabelFor(lblLoyalty2);
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.gridy = firsty;
+ gridBagConstraints.gridwidth = 3;
+ gridBagConstraints.weightx = 1.0;
+ gridBagConstraints.insets = new Insets(0, 10, 0, 0);
+ gridBagConstraints.fill = GridBagConstraints.NONE;
+ gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+ pnlSkills.add(lblLoyalty2, gridBagConstraints);
+
+ firsty++;
+ }
+
+ if ((campaign.getCampaignOptions().isUseFatigue())
+ && (person.getEffectiveFatigue(campaign) > 0)) {
+ lblFatigue1.setName("lblFatigue1");
+ lblFatigue1.setText(resourceMap.getString("lblFatigue1.text"));
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridx = 0;
+ gridBagConstraints.gridy = firsty;
+ gridBagConstraints.fill = GridBagConstraints.NONE;
+ gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+ pnlSkills.add(lblFatigue1, gridBagConstraints);
+
+ StringBuilder fatigueDisplay = new StringBuilder();
+ int effectiveFatigue = person.getEffectiveFatigue(campaign);
+ int fatigueTurnoverModifier = MathUtility.clamp(((person.getFatigue() - 1) / 4) - 1, 0, 3);
+
+ fatigueDisplay.append(person.getFatigue());
+
+ if (person.getFatigue() != effectiveFatigue) {
+ fatigueDisplay.append(" / ").append(effectiveFatigue);
+ }
+
+ if (fatigueTurnoverModifier > 0) {
+ fatigueDisplay.append(" (-").append(fatigueTurnoverModifier).append(')');
+ }
+
+ lblFatigue2.setName("lblFatigue2");
+ lblFatigue2.setText(fatigueDisplay.toString());
+ lblFatigue2.setLabelFor(lblFatigue2);
+ gridBagConstraints = new GridBagConstraints();
+ gridBagConstraints.gridx = 1;
+ gridBagConstraints.gridy = firsty;
+ gridBagConstraints.gridwidth = 3;
+ gridBagConstraints.weightx = 1.0;
+ gridBagConstraints.insets = new Insets(0, 10, 0, 0);
+ gridBagConstraints.fill = GridBagConstraints.NONE;
+ gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
+ pnlSkills.add(lblFatigue2, gridBagConstraints);
+
+ firsty++;
+ }
+
if (campaign.getCampaignOptions().isUseEducationModule()) {
lblEducationLevel1.setName("lblEducationLevel1");
lblEducationLevel1.setText(resourceMap.getString("lblEducationLevel1.text"));
diff --git a/MekHQ/src/mekhq/utilities/MHQXMLUtility.java b/MekHQ/src/mekhq/utilities/MHQXMLUtility.java
index bc14346fc3..bf01998565 100644
--- a/MekHQ/src/mekhq/utilities/MHQXMLUtility.java
+++ b/MekHQ/src/mekhq/utilities/MHQXMLUtility.java
@@ -489,6 +489,10 @@ public static void writeEntityWithCrewToXML(PrintWriter pw, int indentLvl, Entit
crew.append("\" " + MULParser.ATTR_TOUGH + "=\"").append(tgtEnt.getCrew().getToughness(pos));
}
+ if (tgtEnt.getCrew().getCrewFatigue(pos) != 0) {
+ crew.append("\" " + MULParser.ATTR_FATIGUE + "=\"").append(tgtEnt.getCrew().getCrewFatigue(pos));
+ }
+
if (tgtEnt.getCrew().isDead(pos) || tgtEnt.getCrew().getHits(pos) >= Crew.DEATH) {
crew.append("\" " + MULParser.ATTR_HITS + "=\"" + MULParser.VALUE_DEAD + "");
} else if (tgtEnt.getCrew().getHits(pos) > 0) {
diff --git a/MekHQ/unittests/mekhq/campaign/finances/enums/TransactionTypeTest.java b/MekHQ/unittests/mekhq/campaign/finances/enums/TransactionTypeTest.java
index 2e985d3c4e..bb48b9e052 100644
--- a/MekHQ/unittests/mekhq/campaign/finances/enums/TransactionTypeTest.java
+++ b/MekHQ/unittests/mekhq/campaign/finances/enums/TransactionTypeTest.java
@@ -245,12 +245,12 @@ public void testIsRepairs() {
}
@Test
- public void testIsRetirement() {
+ public void testIsPayout() {
for (final TransactionType transactionType : types) {
- if (transactionType == TransactionType.RETIREMENT) {
- assertTrue(transactionType.isRetirement());
+ if (transactionType == TransactionType.PAYOUT) {
+ assertTrue(transactionType.isPayout());
} else {
- assertFalse(transactionType.isRetirement());
+ assertFalse(transactionType.isPayout());
}
}
}
@@ -342,6 +342,17 @@ public void testIsUnitSale() {
}
}
}
+
+ @Test
+ public void testIsTheft() {
+ for (final TransactionType transactionType : types) {
+ if (transactionType == TransactionType.THEFT) {
+ assertTrue(transactionType.isTheft());
+ } else {
+ assertFalse(transactionType.isTheft());
+ }
+ }
+ }
//endregion Boolean Comparison Methods
//region File I/O
@@ -372,7 +383,9 @@ public void testParseFromString() {
assertEquals(TransactionType.REPAIRS, TransactionType.parseFromString("15"));
assertEquals(TransactionType.RANSOM, TransactionType.parseFromString("16"));
assertEquals(TransactionType.EDUCATION, TransactionType.parseFromString("17"));
- assertEquals(TransactionType.MISCELLANEOUS, TransactionType.parseFromString("18"));
+ assertEquals(TransactionType.THEFT, TransactionType.parseFromString("18"));
+ assertEquals(TransactionType.PAYOUT, TransactionType.parseFromString("19"));
+ assertEquals(TransactionType.MISCELLANEOUS, TransactionType.parseFromString("20"));
// Failure Testing
assertEquals(TransactionType.MISCELLANEOUS, TransactionType.parseFromString("failureFailsFake"));
diff --git a/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelStatusTest.java b/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelStatusTest.java
index 379e666e9b..5c18510547 100644
--- a/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelStatusTest.java
+++ b/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelStatusTest.java
@@ -108,12 +108,12 @@ public void testIsOnLeave() {
}
@Test
- public void testIsAWOL() {
+ public void testIsAwol() {
for (final PersonnelStatus personnelStatus : statuses) {
if (personnelStatus == PersonnelStatus.AWOL) {
- assertTrue(personnelStatus.isAWOL());
+ assertTrue(personnelStatus.isAwol());
} else {
- assertFalse(personnelStatus.isAWOL());
+ assertFalse(personnelStatus.isAwol());
}
}
}
@@ -372,9 +372,25 @@ public void testParseFromString() {
assertEquals(PersonnelStatus.MIA, PersonnelStatus.parseFromString("3"));
assertEquals(PersonnelStatus.STUDENT, PersonnelStatus.parseFromString("4"));
assertEquals(PersonnelStatus.MISSING, PersonnelStatus.parseFromString("5"));
+ assertEquals(PersonnelStatus.POW, PersonnelStatus.parseFromString("6"));
+ assertEquals(PersonnelStatus.ON_LEAVE, PersonnelStatus.parseFromString("7"));
+ assertEquals(PersonnelStatus.AWOL, PersonnelStatus.parseFromString("8"));
+ assertEquals(PersonnelStatus.RESIGNED, PersonnelStatus.parseFromString("9"));
+ assertEquals(PersonnelStatus.DESERTED, PersonnelStatus.parseFromString("10"));
+ assertEquals(PersonnelStatus.DEFECTED, PersonnelStatus.parseFromString("11"));
+ assertEquals(PersonnelStatus.HOMICIDE, PersonnelStatus.parseFromString("12"));
+ assertEquals(PersonnelStatus.WOUNDS, PersonnelStatus.parseFromString("13"));
+ assertEquals(PersonnelStatus.DISEASE, PersonnelStatus.parseFromString("14"));
+ assertEquals(PersonnelStatus.ACCIDENTAL, PersonnelStatus.parseFromString("15"));
+ assertEquals(PersonnelStatus.NATURAL_CAUSES, PersonnelStatus.parseFromString("16"));
+ assertEquals(PersonnelStatus.OLD_AGE, PersonnelStatus.parseFromString("17"));
+ assertEquals(PersonnelStatus.MEDICAL_COMPLICATIONS, PersonnelStatus.parseFromString("18"));
+ assertEquals(PersonnelStatus.PREGNANCY_COMPLICATIONS, PersonnelStatus.parseFromString("19"));
+ assertEquals(PersonnelStatus.UNDETERMINED, PersonnelStatus.parseFromString("20"));
+ assertEquals(PersonnelStatus.SUICIDE, PersonnelStatus.parseFromString("21"));
// Error Case
- assertEquals(PersonnelStatus.ACTIVE, PersonnelStatus.parseFromString("6"));
+ assertEquals(PersonnelStatus.ACTIVE, PersonnelStatus.parseFromString("22"));
assertEquals(PersonnelStatus.ACTIVE, PersonnelStatus.parseFromString("blah"));
}
//endregion File I/O
diff --git a/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomRetirementMethodTest.java b/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomRetirementMethodTest.java
deleted file mode 100644
index 4f7acd6fa5..0000000000
--- a/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomRetirementMethodTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved.
- *
- * This file is part of MekHQ.
- *
- * MekHQ is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MekHQ is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MekHQ. If not, see .
- */
-package mekhq.campaign.personnel.enums;
-
-import mekhq.MekHQ;
-import org.junit.jupiter.api.Test;
-
-import java.util.ResourceBundle;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-public class RandomRetirementMethodTest {
- //region Variable Declarations
- private static final RandomRetirementMethod[] methods = RandomRetirementMethod.values();
-
- private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel",
- MekHQ.getMHQOptions().getLocale());
- //endregion Variable Declarations
-
- //region Getters
- @Test
- public void testGetToolTipText() {
- assertEquals(resources.getString("RandomRetirementMethod.NONE.toolTipText"),
- RandomRetirementMethod.NONE.getToolTipText());
- assertEquals(resources.getString("RandomRetirementMethod.AGAINST_THE_BOT.toolTipText"),
- RandomRetirementMethod.AGAINST_THE_BOT.getToolTipText());
- }
- //endregion Getters
-
- //region Boolean Comparison Methods
- @Test
- public void testIsNone() {
- for (final RandomRetirementMethod randomRetirementMethod : methods) {
- if (randomRetirementMethod == RandomRetirementMethod.NONE) {
- assertTrue(randomRetirementMethod.isNone());
- } else {
- assertFalse(randomRetirementMethod.isNone());
- }
- }
- }
-
- @Test
- public void testIsAgainstTheBot() {
- for (final RandomRetirementMethod randomRetirementMethod : methods) {
- if (randomRetirementMethod == RandomRetirementMethod.AGAINST_THE_BOT) {
- assertTrue(randomRetirementMethod.isAgainstTheBot());
- } else {
- assertFalse(randomRetirementMethod.isAgainstTheBot());
- }
- }
- }
- //endregion Boolean Comparison Methods
-
- @Test
- public void testToStringOverride() {
- assertEquals(resources.getString("RandomRetirementMethod.NONE.text"),
- RandomRetirementMethod.NONE.toString());
- assertEquals(resources.getString("RandomRetirementMethod.AGAINST_THE_BOT.text"),
- RandomRetirementMethod.AGAINST_THE_BOT.toString());
- }
-}
diff --git a/MekHQ/unittests/mekhq/campaign/personnel/enums/TurnoverTargetNumberMethodTest.java b/MekHQ/unittests/mekhq/campaign/personnel/enums/TurnoverTargetNumberMethodTest.java
new file mode 100644
index 0000000000..9622ca234b
--- /dev/null
+++ b/MekHQ/unittests/mekhq/campaign/personnel/enums/TurnoverTargetNumberMethodTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved.
+ *
+ * This file is part of MekHQ.
+ *
+ * MekHQ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MekHQ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MekHQ. If not, see .
+ */
+package mekhq.campaign.personnel.enums;
+
+import mekhq.MekHQ;
+import org.junit.jupiter.api.Test;
+
+import java.util.ResourceBundle;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class TurnoverTargetNumberMethodTest {
+ //region Variable Declarations
+ private static final TurnoverTargetNumberMethod[] methods = TurnoverTargetNumberMethod.values();
+
+ private final transient ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel",
+ MekHQ.getMHQOptions().getLocale());
+ //endregion Variable Declarations
+
+ //region Getters
+ @Test
+ public void testGetToolTipText() {
+ assertEquals(resources.getString("TurnoverTargetNumberMethod.FIXED.toolTipText"),
+ TurnoverTargetNumberMethod.FIXED.getToolTipText());
+ assertEquals(resources.getString("TurnoverTargetNumberMethod.ADMINISTRATION.toolTipText"),
+ TurnoverTargetNumberMethod.ADMINISTRATION.getToolTipText());
+ assertEquals(resources.getString("TurnoverTargetNumberMethod.NEGOTIATION.toolTipText"),
+ TurnoverTargetNumberMethod.NEGOTIATION.getToolTipText());
+ }
+ //endregion Getters
+
+ //region Boolean Comparison Methods
+ @Test
+ public void testIsFixed() {
+ for (final TurnoverTargetNumberMethod TurnoverTargetNumberMethod : methods) {
+ if (TurnoverTargetNumberMethod == mekhq.campaign.personnel.enums.TurnoverTargetNumberMethod.FIXED) {
+ assertTrue(TurnoverTargetNumberMethod.isFixed());
+ } else {
+ assertFalse(TurnoverTargetNumberMethod.isFixed());
+ }
+ }
+ }
+
+ @Test
+ public void testIsAdministration() {
+ for (final TurnoverTargetNumberMethod TurnoverTargetNumberMethod : methods) {
+ if (TurnoverTargetNumberMethod == mekhq.campaign.personnel.enums.TurnoverTargetNumberMethod.ADMINISTRATION) {
+ assertTrue(TurnoverTargetNumberMethod.isAdministration());
+ } else {
+ assertFalse(TurnoverTargetNumberMethod.isAdministration());
+ }
+ }
+ }
+
+ @Test
+ public void testIsNegotiation() {
+ for (final TurnoverTargetNumberMethod TurnoverTargetNumberMethod : methods) {
+ if (TurnoverTargetNumberMethod == mekhq.campaign.personnel.enums.TurnoverTargetNumberMethod.NEGOTIATION) {
+ assertTrue(TurnoverTargetNumberMethod.isNegotiation());
+ } else {
+ assertFalse(TurnoverTargetNumberMethod.isNegotiation());
+ }
+ }
+ }
+ //endregion Boolean Comparison Methods
+
+ @Test
+ public void testToStringOverride() {
+ assertEquals(resources.getString("TurnoverTargetNumberMethod.FIXED.text"),
+ TurnoverTargetNumberMethod.FIXED.toString());
+ assertEquals(resources.getString("TurnoverTargetNumberMethod.ADMINISTRATION.text"),
+ TurnoverTargetNumberMethod.ADMINISTRATION.toString());
+ assertEquals(resources.getString("TurnoverTargetNumberMethod.NEGOTIATION.text"),
+ TurnoverTargetNumberMethod.NEGOTIATION.toString());
+ }
+}