Skip to content

Commit

Permalink
Merge pull request #5871 from IllianiCBT/DependentAndAutomaticRemoval
Browse files Browse the repository at this point in the history
Refactored Personnel Cleanup and Random Dependent Removal
  • Loading branch information
HammerGS authored Feb 6, 2025
2 parents b153009 + ea83531 commit a7929f8
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 35 deletions.
4 changes: 3 additions & 1 deletion MekHQ/resources/mekhq/resources/Campaign.properties
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ turnoverPersonnelKilled.text=<html>You have personnel who have left the unit or
divorce.text=%s has divorced %s.

#### Unsorted Campaign Resources
dependentLeavesForce.text=%s is no longer traveling with the force.
dependentLeavesForce.text=%s %s have departed the force.
dependentLeavesForce.dependent.singular=dependent
dependentLeavesForce.dependent.plural=dependents
dependentJoinsForce.text=%s has started traveling with the force.
relativeJoinsForce.text=%s has started traveling with the force. They are %s's %s.
relativeJoinsForceSpouse.text=spouse
Expand Down
102 changes: 68 additions & 34 deletions MekHQ/src/mekhq/campaign/Campaign.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
import mekhq.campaign.personnel.education.Academy;
import mekhq.campaign.personnel.education.EducationController;
import mekhq.campaign.personnel.enums.*;
import mekhq.campaign.personnel.familyTree.Genealogy;
import mekhq.campaign.personnel.generator.*;
import mekhq.campaign.personnel.marriage.AbstractMarriage;
import mekhq.campaign.personnel.marriage.DisabledRandomMarriage;
Expand Down Expand Up @@ -4846,15 +4847,13 @@ public boolean newDay() {
// check for anything in finances
finances.newDay(this, yesterday, getLocalDate());

// process removal of old personnel data on the last day of each month
if ((campaignOptions.isUsePersonnelRemoval())
&& (currentDay.getMonth().length(false) == currentDay.getDayOfMonth())) {
// process removal of old personnel data on the first day of each month
if ((campaignOptions.isUsePersonnelRemoval()) && (currentDay.getDayOfMonth() == 1)) {
processPersonnelRemoval();
}

// this duplicates any turnover information so that it is still available on the
// new day.
// otherwise, it's only available if the user inspects history records
// new day. otherwise, it's only available if the user inspects history records
if (!turnoverRetirementInformation.isEmpty()) {
for (String entry : turnoverRetirementInformation) {
addReport(entry);
Expand Down Expand Up @@ -5003,6 +5002,8 @@ private void processRandomDependents() {
* @return The updated number of dependents.
*/
int dependentsRollForRemoval(List<Person> dependents, int dependentCapacity) {
List<Person> dependentsToRemove = new ArrayList<>();

if (getCampaignOptions().isUseRandomDependentRemoval()) {
for (Person dependent : dependents) {
if (!isRemovalEligible(dependent, currentDay)) {
Expand All @@ -5018,12 +5019,34 @@ int dependentsRollForRemoval(List<Person> dependents, int dependentCapacity) {
int targetNumber = 5 - getAtBUnitRatingMod();

if (roll <= targetNumber) {
addReport(String.format(resources.getString("dependentLeavesForce.text"),
dependent.getFullTitle()));
dependentsToRemove.add(dependent);

Genealogy genealogy = dependent.getGenealogy();
for (Person child : genealogy.getChildren()) {
if (child.isChild(currentDay)) {
dependentsToRemove.add(child);
}
}

removePerson(dependent, false);
Person spouse = genealogy.getSpouse();
if (spouse.isDependent()) {
dependentsToRemove.add(spouse);
}
}
}

if (!dependentsToRemove.isEmpty()) {
String pluralizer = dependentsToRemove.size() == 1
? resources.getString("dependentLeavesForce.dependent.singular")
: resources.getString("dependentLeavesForce.dependent.plural");

addReport(String.format(resources.getString("dependentLeavesForce.text"),
dependentsToRemove.size(), pluralizer));
}

for (Person dependent : dependentsToRemove) {
dependent.changeStatus(this, currentDay, PersonnelStatus.LEFT);
}
}

return dependents.size();
Expand Down Expand Up @@ -5097,7 +5120,7 @@ public void processPersonnelRemoval() {
PersonnelStatus status = person.getStatus();

if (status.isDepartedUnit()) {
if (shouldRemovePerson(person, status)) {
if (shouldRemovePerson(person)) {
personnelToRemove.add(person);
}
}
Expand All @@ -5114,39 +5137,50 @@ public void processPersonnelRemoval() {

/**
* Determines whether a person's records should be removed from the campaign
* based on their status and retirement month.
* based on their retirement date, date of death, personnel status, and genealogy activity.
*
* <p>The method evaluates the following conditions in order:
* <ul>
* <li>If the person has a retirement date and retirees are exempt from removal as per
* campaign options, the method returns {@code false}.</li>
* <li>If the person has a date of death, and cemeteries are exempt from removal as per
* campaign options, the method returns {@code false}.</li>
* <li>If the person has an active genealogy, the method returns {@code false}.</li>
* <li>If the person's retirement date is more than one month ago, the method returns {@code true}.</li>
* <li>If the person's date of death is more than one month ago, the method returns {@code true}.</li>
* </ul>
*
* <p>If none of the above conditions are met, the method returns {@code false}.
*
* @param person The individual being checked.
* @param status The personnel status of the individual.
* @return true if the person should be removed, false otherwise.
* @return {@code true} if the person should be removed, {@code false} otherwise.
*/
private boolean shouldRemovePerson(Person person, PersonnelStatus status) {
// don't remove a character if they are related to a genealogy
// with at least one member still present in the campaign
Map<FamilialRelationshipType, List<Person>> family = person.getGenealogy().getFamily();

if (family.keySet().stream()
.flatMap(relationshipType -> family.get(relationshipType).stream())
.anyMatch(relation -> relation.getStatus().isDepartedUnit())) {
private boolean shouldRemovePerson(Person person) {
// We do these checks first, as they're cheaper than parsing the entire genealogy
LocalDate retirementDate = person.getRetirement();
if (retirementDate != null && campaignOptions.isUseRemovalExemptRetirees()) {
return false;
}

int retirementMonthValue;
LocalDate deathDate = person.getDateOfDeath();
if (deathDate != null && campaignOptions.isUseRemovalExemptCemetery()) {
return false;
}

if (person.getRetirement() != null) {
retirementMonthValue = person.getRetirement().getMonthValue();
} else {
person.setRetirement(getLocalDate());
// Do not remove if the character has an active genealogy
Genealogy genealogy = person.getGenealogy();

if (genealogy.isActive()) {
return false;
}

// return true if the individual has left the campaign for over a month
// *AND*
// is dead (and we're not exempting the cemetery) *OR* is retired (and we're not
// exempting retirees)
return (retirementMonthValue < (getLocalDate().getMonthValue() + 1)) &&
(((status.isDead()) && (!campaignOptions.isUseRemovalExemptCemetery()))
|| ((status.isRetired()) && (!campaignOptions.isUseRemovalExemptRetirees())));
// Did the departure occur more than a month ago?
LocalDate aMonthAgo = currentDay.minusMonths(1);
if (retirementDate != null && retirementDate.isBefore(aMonthAgo)) {
return true;
}
return deathDate != null && deathDate.isBefore(aMonthAgo);
}

/**
Expand Down Expand Up @@ -9046,7 +9080,7 @@ public void writePartInUseMapToXML(final PrintWriter pw, int indent) {
}
}

/**
/**
* Wipes the Parts in use map for the purpose of resetting all values to their default
*/
public void wipePartsInUseMap() {
Expand All @@ -9072,13 +9106,13 @@ public ImageIcon getCampaignFactionIcon() {
}
return icon;
}

/**
* Checks if another active scenario has this scenarioID as it's linkedScenarioID and returns true if it finds one.
*/
public boolean checkLinkedScenario(int scenarioID) {
for (Scenario scenario : getScenarios()) {
if ((scenario.getLinkedScenario() == scenarioID)
if ((scenario.getLinkedScenario() == scenarioID)
&& (getScenario(scenario.getId()).getStatus().isCurrent())) {
return true;
}
Expand Down
23 changes: 23 additions & 0 deletions MekHQ/src/mekhq/campaign/personnel/familyTree/Genealogy.java
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,29 @@ public void clearGenealogyLinks() {
}
}

/**
* Checks if there is at least one person in the family who is not marked as "departed".
*
* <p>The method iterates through all relationship groups in the {@code family} map and checks
* the status of each person. If any person is found whose status is not marked as "departed",
* the method returns {@code true}. Otherwise, it returns {@code false} once all groups have
* been checked.
*
* @return {@code true} if at least one person in the family is active (not "departed"),
* {@code false} otherwise.
*/
public boolean isActive() {
for (List<Person> relationshipGroup : family.values()) {
for (Person relation : relationshipGroup) {
if (!relation.getStatus().isDepartedUnit()) {
return true;
}
}
}

return false;
}

// region File I/O
/**
* @param pw the PrintWriter to write to
Expand Down

0 comments on commit a7929f8

Please sign in to comment.