diff --git a/MekHQ/resources/mekhq/resources/Campaign.properties b/MekHQ/resources/mekhq/resources/Campaign.properties index 4f99d2dde7..e48ef36fce 100644 --- a/MekHQ/resources/mekhq/resources/Campaign.properties +++ b/MekHQ/resources/mekhq/resources/Campaign.properties @@ -75,6 +75,9 @@ divorce.text=%s has divorced %s. #### Unsorted Campaign Resources dependentLeavesForce.text=%s is no longer travelling with the force, and is thus no longer dependent on it. dependentJoinsForce.text=%s has started traveling with the force, and is now dependent on it. +relativeJoinsForce.text=%s, %s's %s, has started traveling with the force, and is now dependent on it. +relativeJoinsForceSpouse.text=spouse +relativeJoinsForceChild.text=child bonusPartLog.text=Bonus part used to acquire 1x newAtBScenario.format=New scenario "{0}" will occur on {1}. atbScenarioToday.format=Scenario "{0}" is today, deploy a force from your TOE! diff --git a/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties b/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties index 2cd8013647..1dcaa7ae5d 100644 --- a/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties +++ b/MekHQ/resources/mekhq/resources/CampaignOptionsDialog.properties @@ -435,6 +435,11 @@ lifePathsPanel.title=Life Paths personnelRandomizationPanel.title=Personnel Randomization chkUseDylansRandomXP.text=Use Dylan's Random XP (Unofficial) chkUseDylansRandomXP.toolTipText=Use Dylan's optional random XP on creation of a new person (20% chance each of 0, 1, 2, 3, and randomized between 1 and 8 XP) +lblNonBinaryDiceSize.text=Non-Binary Personnel Dice Size +lblNonBinaryDiceSize.toolTipText=This is the number of sides on the die rolled to determine whether a character's random gender is 'Other.' A character will identify as a gender other than Male or Female on a roll of 1. Set to 0 to disable non-binary characters. The default value is based on the 2022 US Census and gives a result of around 1.6%. + +# Random Histories +randomHistoriesPanel.title=Random Histories # Random Histories randomHistoriesPanel.title=Random Histories @@ -446,47 +451,57 @@ chkUseIntelligenceXpMultiplier.text=Use Intelligence XP Modifiers chkUseIntelligenceXpMultiplier.toolTipText=If enabled, a character's intelligence will influence all XP costs. chkUseRandomPersonalityReputation.text=Personalities Influence Unit Reputation chkUseRandomPersonalityReputation.toolTipText=if enabled, the personality of the campaign commander will impact the unit's Reputation. +chkUseSimulatedRelationships.text=Simulate Relationship History +chkUseSimulatedRelationships.toolTipText=Personnel are generated with a random relationship history.\ +
\ +
If randomized marriages are enabled the various marriage settings (including chance) will be used to determine whether new personnel have been previously married.\ +
\ +
If randomized procreation is enabled the various procreation settings (including chance) will be used to determine whether new personnel have children traveling with them.\ +
\ +
If randomized divorce is enabled the various divorce settings (including chance) will be used to determine whether any marriages have ended in divorce.\ +
\ +
Any children and current spouses will travel alongside the unit. # Family -familyPanel.title=Family (Unofficial) +familyPanel.title=Family lblFamilyDisplayLevel.text=The Level of Relation to be Displayed in the Personnel Panel lblFamilyDisplayLevel.toolTipText=This setting is the relation to the selected person that MekHQ will display up to, including the previous levels
(higher levels require more processing when loading a person in the personnel table) # Marriage -marriagePanel.title=Marriage +marriagePanel.title=Marriage (Unofficial) chkUseManualMarriages.text=Use Manual Marriages chkUseManualMarriages.toolTipText=This allows the player to disable the Choose Spouse (Mate) option in the personnel table mouse adapter.
This option is provided for performance reasons for extremely large campaigns (10k+ personnel). chkUseClanPersonnelMarriages.text=Use Clan Personnel Marriages chkUseClanPersonnelMarriages.toolTipText=Allow clan-origin personnel to marry other personnel. chkUsePrisonerMarriages.text=Use Prisoner Marriages chkUsePrisonerMarriages.toolTipText=Allow prisoners to marry other prisoners, and manually marrying a prisoner to a free member of the force. Bondsmen are treated as free personnel when it comes to this option, and are thus not affected by it. -lblMinimumMarriageAge.text=Minimum Marital Age -lblMinimumMarriageAge.toolTipText=This is the minimum age that is allowed for a person to be married. +lblNoInterestInMarriageDiceSize.text=No Interest in Marriage Dice Size +lblNoInterestInMarriageDiceSize.toolTipText=This is the number of sides on the die rolled to determine whether a character has no interest in marriage. This die is rolled when a character is created, with no interest in marriage being determined on a roll of 1. This can be overridden by right-clicking on the character and changing their 'Interested in Marriage' flag. lblCheckMutualAncestorsDepth.text=Minimum Mutual Ancestor Check Depth for Marriage to be Possible -lblCheckMutualAncestorsDepth.toolTipText=This is the depth to which the ancestry of two people are checked for mutual ancestors to determine if they can marry.
Set to 0 to disable the ancestry check. +lblCheckMutualAncestorsDepth.toolTipText=This is the depth to which the ancestry of two people is checked for mutual ancestors to determine if they can marry.
Set to 0 to disable the ancestry check. chkLogMarriageNameChanges.text=Log Marriage Name Changes chkLogMarriageNameChanges.toolTipText=This enables the addition of a personnel log entry whenever a name is changed during marriage. marriageSurnameWeightsPanel.title=Marriage Surname Weights marriageSurnameWeightsPanel.toolTipText=These are the weights used in determining the surname change, if any, when using the Weighted Marriage option.
They will be the percent chance if the values for the weights all add up to 100. -randomMarriagePanel.title=Random Marriages (Unofficial) +randomMarriagePanel.title=Random Marriages lblRandomMarriageMethod.text=Random Marriage Method lblRandomMarriageMethod.toolTipText=This is the method used to determine if an eligible person randomly marries. -chkUseRandomSameSexMarriages.text=Use Same-sex Random Marriage -chkUseRandomSameSexMarriages.toolTipText=This enables random marriages between fitting same sex members of your force. chkUseRandomClanPersonnelMarriages.text=Use Random Clan Personnel Marriages chkUseRandomClanPersonnelMarriages.toolTipText=Allow clan-origin personnel to randomly marry other personnel. chkUseRandomPrisonerMarriages.text=Use Random Prisoner Marriages chkUseRandomPrisonerMarriages.toolTipText=Allow prisoners to randomly marry other prisoners. Bondsmen are treated as free personnel when it comes to this option, and are thus not affected by it. lblRandomMarriageAgeRange.text=Random Marriage Age Range -lblRandomMarriageAgeRange.toolTipText=This plus/minus the age forms the possible range of ages for spouses in the forming of a random marriage. -percentageRandomMarriagePanel.title=Percentage Random Marriage -lblPercentageRandomMarriageOppositeSexChance.text=Opposite Sex Chance -lblPercentageRandomMarriageOppositeSexChance.toolTipText=This is the percent chance that a fitting member of your force will marry another fitting member of your force of the opposite sex. -lblPercentageRandomMarriageSameSexChance.text=Same Sex Chance -lblPercentageRandomMarriageSameSexChance.toolTipText=This is the percent chance that a fitting member of your force will marry another fitting member of your force of the same sex. +lblRandomMarriageAgeRange.toolTipText=This plus/minus age forms the possible range of ages for spouses in the forming of a random marriage. +percentageRandomMarriagePanel.title=Marriage Dice +lblRandomMarriageOppositeSexDiceSize.text=Opposite Sex Die Size +lblRandomMarriageOppositeSexDiceSize.toolTipText=This determines the number of sides on the die rolled to determine whether a character gets married. Marriage occurs on a roll of 1. +lblRandomSameSexMarriageDiceSize.text=Same Sex Die Size +lblRandomSameSexMarriageDiceSize.toolTipText=This determines the number of sides on the die rolled to determine whether a marriage is same-sex. Same-sex marriage occurs on a roll of 1. Set this value to 0 to disable same-sex marriages. The default value is based on real world data. For additional information, please see the documentation included in `MekHQ/docs/personnel modules`. +lblRandomNewDependentMarriage.text=Inter-Unit Marriage Die Size +lblRandomNewDependentMarriage.toolTipText=This determines the number of sides on the die rolled to determine whether a marriage is another character in the campaign unit. Inter-unit marriage occurs on a roll of 1. Set this value to 0 to disable inter-unit marriages. # Divorce -divorcePanel.title=Divorce +divorcePanel.title=Divorce (Unofficial) chkUseManualDivorce.text=Use Manual Divorce chkUseManualDivorce.toolTipText=This allows the player to disable the Remove Spouse option in the personnel table mouse adapter. chkUseClanPersonnelDivorce.text=Use Clan Personnel Divorce @@ -495,7 +510,7 @@ chkUsePrisonerDivorce.text=Use Prisoner Divorce chkUsePrisonerDivorce.toolTipText=Allow divorce when one or both of the couple are currently prisoners. Bondsmen are treated as free personnel when it comes to this option, and are thus not affected by it. divorceSurnameWeightsPanel.title=Divorce Surname Weights divorceSurnameWeightsPanel.toolTipText=These are the weights used in determining the surname change, if any, when using the Weighted Divorce option.
They will be the percent chance if the values for the weights all add up to 100. -randomDivorcePanel.title=Random Divorce (Unofficial) +randomDivorcePanel.title=Random Divorce lblRandomDivorceMethod.text=Random Divorce Method lblRandomDivorceMethod.toolTipText=This is the method used to determine if an eligible person randomly divorces. chkUseRandomOppositeSexDivorce.text=Use Random Opposite Sex Divorce @@ -506,11 +521,9 @@ chkUseRandomClanPersonnelDivorce.text=Use Random Clan Personnel Divorce chkUseRandomClanPersonnelDivorce.toolTipText=Allow clan-origin personnel to randomly divorce, whether as the origin or the spouse. chkUseRandomPrisonerDivorce.text=Use Random Prisoner Divorce chkUseRandomPrisonerDivorce.toolTipText=Allow random divorce when one or both of the couple are currently prisoners. Bondsmen are treated as free personnel when it comes to this option, and are thus not affected by it. -percentageRandomDivorcePanel.title=Percentage Random Divorce -lblPercentageRandomDivorceOppositeSexChance.text=Opposite Sex Chance -lblPercentageRandomDivorceOppositeSexChance.toolTipText=This is the percent chance that a fitting member of your force will divorce their opposite sex spouse. -lblPercentageRandomDivorceSameSexChance.text=Same Sex Chance -lblPercentageRandomDivorceSameSexChance.toolTipText=This is the percent chance that a fitting member of your force will divorce their same-sex spouse. +percentageRandomDivorcePanel.title=Divorce Dice +lblRandomDivorceDiceSize.text=Divorce Die Size +lblRandomDivorceDiceSize.toolTipText=This is the number of sides featured on the weekly dice rolled to see whether a marriage ends in divorce. Divorce occurs on a roll of 1. Set to 0 to disable random divorces for all personnel. # Procreation procreationPanel.title=Procreation (Unofficial) @@ -533,6 +546,10 @@ chkDetermineFatherAtBirth.text=Determine Father at Birth instead of Conception chkDetermineFatherAtBirth.toolTipText=The father of a child will be determined based on the spouse at the birth of the child, followed by the spouse at time of conception, followed by nobody.
This is opposed to just using the spouse, if any, at the time of conception. chkDisplayTrueDueDate.text=Display True Due Date chkDisplayTrueDueDate.toolTipText=This displays the actual date the baby will be delivered on the mother's personnel sheet instead of an estimated due date. +lblNoInterestInChildrenDiceSize.text=No Interest in Children Die Size +lblNoInterestInChildrenDiceSize.toolTipText=This is the number of sides on the die rolled to determine whether a character has no interest in children. This die is rolled when creating the character, with no interest occurring on a roll of 1. The results of this roll can be changed by right-clicking on the character and changing the 'Interested in Children' flag. Changing this value to 1 will mean all characters are interested in children. +chkUseMaternityLeave.text=Use Automatic Maternity Leave +chkUseMaternityLeave.toolTipText=If enabled, pregnant personnel will be placed on maternity leave 20 weeks before they give birth and will not return to active duty until 6 after they have given birth. chkLogProcreation.text=Log Conception and Birth in Personnel and Medical Logs chkLogProcreation.toolTipText=This enables logging the date of conception and birth in a person's logs. randomProcreationPanel.title=Random Procreation @@ -544,11 +561,11 @@ chkUseRandomClanPersonnelProcreation.text=Use Random Clan Personnel Procreation chkUseRandomClanPersonnelProcreation.toolTipText=Allow clan-origin personnel to randomly procreate. chkUseRandomPrisonerProcreation.text=Use Random Prisoner Procreation chkUseRandomPrisonerProcreation.toolTipText=Allow prisoners to randomly procreate. Bondsmen are treated as free personnel when it comes to this option, and are thus not affected by it. -percentageRandomProcreationPanel.title=Percentage Random Procreation -lblPercentageRandomProcreationRelationshipChance.text=Relationship Chance -lblPercentageRandomProcreationRelationshipChance.toolTipText=This is the percent chance per day that a female member of your force in a relationship will have a baby when not deployed. -lblPercentageRandomProcreationRelationshiplessChance.text=Relationshipless Chance -lblPercentageRandomProcreationRelationshiplessChance.toolTipText=This is the percent chance per day that a female member of your force not in a relationship will have a baby when not deployed. +percentageRandomProcreationPanel.title=Procreation Dice +lblRandomProcreationRelationshipDiceSize.text=Relationship Die Size +lblRandomProcreationRelationshipDiceSize.toolTipText=This is the number of sides on the dice rolled weekly to determine whether a woman in a relationship will fall pregnant. A roll of 1 results in a pregnancy. +lblRandomProcreationRelationshiplessDiceSize.text=Relationshipless Die Size +lblRandomProcreationRelationshiplessDiceSize.toolTipText=This is the number of sides on the dice rolled weekly to determine whether a woman not in a relationship will fall pregnant. A roll of 1 results in a pregnancy. # Death deathPanel.title=Death @@ -719,7 +736,6 @@ chkOverageRepaymentInFinalPayment.toolTipText=This is an unofficial addition tha lblXpCostMultiplier.text=XP Cost Multiplier lblXpCostMultiplier.tooltip=This value multiplies the XP costs for SPA and Skill Levels. lblScenarioXP.text=XP for each completed scenario -lblKillXP.text=XP for every lblXPForEvery.text=XP for every lblKills.text=kills lblTasks.text=successful tasks diff --git a/MekHQ/resources/mekhq/resources/GUI.properties b/MekHQ/resources/mekhq/resources/GUI.properties index 41c3d1b061..25f8dc3623 100644 --- a/MekHQ/resources/mekhq/resources/GUI.properties +++ b/MekHQ/resources/mekhq/resources/GUI.properties @@ -103,6 +103,8 @@ execute.text=Execute jettison.text=Jettison recruit.text=Recruit abtakha.text=Adopt (Abtakha) +adopt.text=Adopt +adopt.description=%s, %s, Age %s changePrimaryRole.text=Change Primary Role changeSecondaryRole.text=Change Secondary Role setSalary.text=Set Salary @@ -210,7 +212,7 @@ miFounder.toolTipText=If this is selected, then the person is a founding member miImmortal.text=Immortal miImmortal.toolTipText=If this is selected, then the person will not be processed during random death \n(standard checks still apply) miMarriageable.text=Marriageable -miMarriageable.toolTipText=If this is selected, then the person will be included as a potential spouse for marriages (married personnel will not be included even if this flag is selected, nor will characters under %s years old) +miMarriageable.toolTipText=If this is selected, then the person will be included as a potential spouse for marriages (married personnel will not be included even if this flag is selected, nor will characters under 18 years old) miTryingToConceive.text=Trying to Conceive miTryingToConceive.toolTipText=If this is selected, the person has a chance to have children created through random procreation (this flag is ignored for personnel under 18 years old). @@ -284,7 +286,7 @@ addMinimumComplement.text=Add minimum complement ##### Base Components - These may originate in MM, but are in active use #### AbstractMHQNagDialog -chkIgnore.text=Do not bother me again +chkIgnore.text=Don't bother me again chkIgnore.toolTipText=If selected, this nag will no longer show until re-enabled under Suite Options. @@ -301,7 +303,7 @@ StandardForceIconDialog.title=Select Force Icon ### UnitIconDialog Class UnitIconDialog.title=Select Unit Icon -UnitIconDialog.btnNone.toolTipText=The unit does not have a unit icon. This will hide all displays of the unit icon outside Campaign Options. +UnitIconDialog.btnNone.toolTipText=The unit doesn't have a unit icon. This will hide all displays of the unit icon outside Campaign Options. #### Nag Dialogs ### InsufficientAstechsNagDialog Class @@ -322,7 +324,7 @@ OutstandingScenariosNagDialog.text=You have a pending battle. Failure to deploy ### ShortDeploymentNagDialog Class ShortDeploymentNagDialog.title=Unmet Deployment Requirements -ShortDeploymentNagDialog.text=You have not met the deployment levels required by your contracts. Do you really wish to advance the day? +ShortDeploymentNagDialog.text=You haven't met the deployment levels required by your contracts. Do you really wish to advance the day? ### UnmaintainedUnitsNagDialog Class UnmaintainedUnitsNagDialog.title=Unmaintained Units @@ -462,7 +464,7 @@ chkSpecifyFaction.text=Specify Starting Faction chkSpecifyFaction.toolTipText=This allows you to specify the faction a campaign will start with. The default starting faction is Mercenary. chkSpecifyPlanet.text=Specify Starting Planet chkSpecifyPlanet.toolTipText=This allows you to specify the starting planet for a campaign. The default starting planet is otherwise the starting planet per faction as per /data/universe/factions.xml. -chkStartingSystemFactionSpecific.toolTipText=Filter the starting planet options, so they are specific to the selected starting faction. +chkStartingSystemFactionSpecific.toolTipText=Filter the starting planet options, so they're specific to the selected starting faction. comboStartingSystem.toolTipText=This is the system to select the starting planet from. comboStartingPlanet.toolTipText=This is the planet the campaign will start at. chkSpecifyRankSystem.text=Specify Rank System @@ -486,7 +488,7 @@ btnGameOptions.toolTipText=This lets you view the current MegaMek game options. chkSpecifyCampaignOptions.text=Specify Campaign Options chkSpecifyCampaignOptions.toolTipText=This allows you to specify the MekHQ campaign options. If this is left empty, the default options will be kept. ## Button Actions -blankPresetName.text=The entered preset name cannot be blank. +blankPresetName.text=The entered preset name can't be blank. nullFactionSpecified.text=The specified faction is non-existent. Do you want to continue ignoring the faction specification? nullPlanetSpecified.text=The specified planet is non-existent. Do you want to continue ignoring the planet specification? nullRankSystemSpecified.text=The specified rank system is non-existent. Do you want to continue ignoring the rank system specification? @@ -496,7 +498,7 @@ CustomRankSystemCreationDialog.title=Custom Rank System Creation lblRankSystemCode.text=Rank System Code lblRankSystemCode.toolTipText=This is the internal code used to process rank systems. This should be a handful of capital letters, although they will be capitalized automatically if this is missed. lblRankSystemName.text=Rank System Name -lblRankSystemName.toolTipText=This is the name of the rank system. It cannot be blank. +lblRankSystemName.toolTipText=This is the name of the rank system. It can't be blank. lblRankSystemDescription.text=Rank System Description lblRankSystemDescription.toolTipText=This is a description of the rank system. chkUseROMDesignation.text=Use ROM Designation @@ -504,12 +506,12 @@ chkUseROMDesignation.toolTipText=The rank system uses ComStar's ROM branch desig chkUseManeiDomini.text=Use Manei Domini Class/Rank chkUseManeiDomini.toolTipText=The rank system uses the Word of Blake's Manei Domini Class and Rank designations. lblRankSystemType.text=Rank System Type -lblRankSystemType.toolTipText=This is the type for the rank system, which is used to determine how they are saved and processed. +lblRankSystemType.toolTipText=This is the type for the rank system, which is used to determine how they're saved and processed. chkSwapToRankSystem.text=Swap to Rank System upon Creation chkSwapToRankSystem.toolTipText=This will swap you to having this rank system when working with rank systems. -CustomRankSystemCreationDialog.BlankRankSystemCode.text=The entered rank system code cannot be blank. -CustomRankSystemCreationDialog.BlankRankSystemName.text=The entered rank system name cannot be blank. -CustomRankSystemCreationDialog.DuplicateCode.text=The entered rank system code cannot duplicate an existing code, as all rank systems must have a unique system code. +CustomRankSystemCreationDialog.BlankRankSystemCode.text=The entered rank system code can't be blank. +CustomRankSystemCreationDialog.BlankRankSystemName.text=The entered rank system name can't be blank. +CustomRankSystemCreationDialog.DuplicateCode.text=The entered rank system code can't duplicate an existing code, as all rank systems must have a unique system code. ### DataLoadingDialog Class DataLoadingDialog.title=Data Loading @@ -531,9 +533,9 @@ DataLoadingDialog.NullEntityException.text=The following units could not be load DataLoadingDialog.NullPointerException.title=Null Exception DataLoadingDialog.NullPointerException.text=The save has failed to load for the following reason: \n%s \n\nIf this has been caused by a missing academy, please be sure to copy over any custom academy sets before starting a new version of MekHQ. \n\nIf you believe the academies listed are not customs, then please load your campaign into the last working version of MekHQ and set all students attending the affected academy to the 'Active' status. DataLoadingDialog.OutOfMemoryError.title=Not Enough Memory -DataLoadingDialog.OutOfMemoryError.text=MekHQ ran out of memory attempting to load the campaign file. \nTry increasing the memory allocated to MekHQ and reloading. \nSee the FAQ at http://megamek.org/ for details. +DataLoadingDialog.OutOfMemoryError.text=MekHQ ran out of memory attempting to load the campaign file. \nTry increasing the memory allocated to MekHQ and reloading. \nSee the FAQ at https://megamek.org/ for details. DataLoadingDialog.ExecutionException.title=Campaign Loading Error -DataLoadingDialog.ExecutionException.text=The campaign file could not be loaded. \nPlease check the MekHQ.log file for details. +DataLoadingDialog.ExecutionException.text=The campaign file couldn't be loaded. \nPlease check the MekHQ.log file for details. ### GMToolsDialog Class GMToolsDialog.title=GM Tools @@ -557,7 +559,7 @@ lblWeight.text=Weight btnRollRAT.text=Roll For RAT btnRollRAT.toolTipText=Roll a unit on the selected RATs or the force generator (based on Campaign Options settings) using the provided values. btnAddUnit.text=Add Unit -btnAddUnit.toolTipText=Add the unit to the campaign at no cost, assigning them to the current person if loaded with a person, and they do not have a unit assigned. +btnAddUnit.toolTipText=Add the unit to the campaign at no cost, assigning them to the current person if loaded with a person, and they don't have a unit assigned. invalidYear.error=Please enter a valid year noValidUnit.error=No unit matching criteria and purchase restrictions. entityLoadFailure.error=Failed to load entity %s from %s @@ -689,8 +691,6 @@ optionHealedInjuriesForeground.text=Healed Injuries Foreground optionHealedInjuriesBackground.text=Healed Injuries Background optionPregnantForeground.text=Pregnant Foreground optionPregnantBackground.text=Pregnant Background -optionPaidRetirementForeground.text=Paid Retirement Foreground -optionPaidRetirementBackground.text=Paid Retirement Background optionStratConHexCoordForeground.text=StratCon Hex Coordinate Foreground optionFontColorNegative.text=Font Color Negative Event optionFontColorWarning.text=Font Color Warning @@ -737,11 +737,11 @@ optionPregnantCombatantNag.toolTipText=This allows you to ignore the daily warni optionPrisonersNag.text=Hide Prisoners of War Nag optionPrisonersNag.toolTipText=This allows you to ignore the daily warning for when you have prisoners of war outside of a contract. optionUntreatedPersonnelNag.text=Hide Untreated Personnel Nag -optionUntreatedPersonnelNag.toolTipText=This allows you to ignore the daily warning for when you have wounded personnel that have not been assigned to a doctor. +optionUntreatedPersonnelNag.toolTipText=This allows you to ignore the daily warning for when you've wounded personnel that haven't been assigned to a doctor. optionNoCommanderNag.text=Hide No Commander Nag -optionNoCommanderNag.toolTipText=This allows you to ignore the daily warning for when you do not have someone assigned as the overall force commander. +optionNoCommanderNag.toolTipText=This allows you to ignore the daily warning for when you don't have someone assigned as the overall force commander. optionContractEndedNag.text=Hide Contract Ended Nag -optionContractEndedNag.toolTipText=This allows you to ignore the daily warning for when you do not have a completed contract still active in the briefing tab. +optionContractEndedNag.toolTipText=This allows you to ignore the daily warning for when you don't have a completed contract still active in the briefing tab. optionInsufficientAstechsNag.text=Hide Insufficient Astechs Nag optionInsufficientAstechsNag.toolTipText=This allows you to ignore the daily warning for when you don't have enough astechs to support your techs. optionInsufficientAstechTimeNag.text=Hide Insufficient Astech Time Nag @@ -768,7 +768,7 @@ lblUserDir.text=User Files Directory: lblUserDir.toolTipText=Use this directory for resources you want to share between different installations or versions of MegaMek, MegaMekLab and MekHQ. Fonts, units, camos, portraits and fluff images will also be loaded from this directory.
Note: Inside the user directory, use the directory structure of MM/MML/MHQ for camos, portraits and fluff images, i.e. data/images/camo, data/images/portraits and data/images/fluff/.
Fonts and units can be placed anywhere in the user directory. userDirChooser.title=Choose User Data Folder lblStartGameDelay.text=Start Game Base Delay (ms) -lblStartGameDelay.toolTipText=This is the base start game delay, in milliseconds.
Increase this value when settings are not being set properly, the board isn't being loaded, and / or planetary conditions aren't loading properly.
This is limited to values between 250 and 2,500, with a default value of 1,000. +lblStartGameDelay.toolTipText=This is the base start game delay, in milliseconds.
Increase this value when settings aren't being set properly, the board isn't being loaded, and / or planetary conditions aren't loading properly.
This is limited to values between 250 and 2,500, with a default value of 1,000. lblStartGameClientDelay.text=Start Game Client Delay (ms) lblStartGameClientDelay.toolTipText=Client start game delay time in milliseconds.
Increase this value when the "Client has not finished initialization, and is currently in an unknown phase" error message is repeatedly logged without progressing past that point.
If this causes timeout issues, decrease this value and increase the client retry count instead.
This is limited to values between 50 and 2,500, with a default value of 50. lblStartGameClientRetryCount.text=Start Game Client Retry Count @@ -890,27 +890,27 @@ PersonnelFilter.FOUNDER.toolTipText=Display personnel with the Founder tag. PersonnelFilter.PRISONER.text=Prisoners PersonnelFilter.PRISONER.toolTipText=Display prisoners and bondsmen. PersonnelFilter.INACTIVE.text=Inactive Personnel -PersonnelFilter.INACTIVE.toolTipText=Display personnel who are currently inactive. +PersonnelFilter.INACTIVE.toolTipText=Display personnel who're currently inactive. PersonnelFilter.ON_LEAVE.text=Personnel on Leave PersonnelFilter.ON_LEAVE.toolTipText=Display personnel who are currently on leave. PersonnelFilter.MIA.text=MIA & PoW Personnel -PersonnelFilter.MIA.toolTipText=Display personnel who are currently missing in action or prisoners of the enemy. +PersonnelFilter.MIA.toolTipText=Display personnel who're currently missing in action or prisoners of the enemy. PersonnelFilter.RETIRED.text=Retired Personnel PersonnelFilter.RETIRED.toolTipText=Display retired personnel. PersonnelFilter.RESIGNED.text=Resigned Personnel -PersonnelFilter.RESIGNED.toolTipText=Display personnel who have resigned. +PersonnelFilter.RESIGNED.toolTipText=Display personnel who've resigned. PersonnelFilter.DESERTED.text=Deserters & Defectors PersonnelFilter.DESERTED.toolTipText=Display former personnel who deserted or defected. PersonnelFilter.AWOL.text=AWOL Personnel -PersonnelFilter.AWOL.toolTipText=Display personnel who are currently AWOL. +PersonnelFilter.AWOL.toolTipText=Display personnel who're currently AWOL. PersonnelFilter.STUDENT.text=Students PersonnelFilter.STUDENT.toolTipText=Display personnel that are currently undergoing a period of education or training PersonnelFilter.MISSING.text=Missing PersonnelFilter.MISSING.toolTipText=Display personnel that are currently missing PersonnelFilter.KIA.text=Rolls of Honor -PersonnelFilter.KIA.toolTipText=Display personnel who have been killed in action. +PersonnelFilter.KIA.toolTipText=Display personnel who've been killed in action. PersonnelFilter.DEAD.text=Cemetery -PersonnelFilter.DEAD.toolTipText=Display personnel who have passed away. +PersonnelFilter.DEAD.toolTipText=Display personnel who've passed away. #### PersonnelFilterStyle Enum PersonnelFilterStyle.STANDARD.text=Standard @@ -979,9 +979,9 @@ PersonnelTableModelColumn.DEATH_DATE.text=Death Date PersonnelTableModelColumn.COMMANDER.text=Commander PersonnelTableModelColumn.FOUNDER.text=Founder PersonnelTableModelColumn.CLAN_PERSONNEL.text=Clan Personnel -PersonnelTableModelColumn.MARRIAGEABLE.text=Marriageable +PersonnelTableModelColumn.MARRIAGEABLE.text=Interested in Marriage PersonnelTableModelColumn.DIVORCEABLE.text=Divorceable -PersonnelTableModelColumn.TRYING_TO_CONCEIVE.text=Trying to Conceive +PersonnelTableModelColumn.TRYING_TO_CONCEIVE.text=Interested in Children PersonnelTableModelColumn.IMMORTAL.text=Immortal PersonnelTableModelColumn.TOUGHNESS.text=Toughness PersonnelTableModelColumn.FATIGUE.text=Fatigue @@ -1115,11 +1115,11 @@ lblLanceSize.toolTipText=The number of BattleMeks to generate per lance, from 3 ### Personnel Panel personnelPanel.title=Personnel lblTotalSupportPersonnel.text=Number of Support Personnel: %d -lblTotalSupportPersonnel.toolTipText=This is the maximum number of starting support personnel for the force. This does not include assistants. +lblTotalSupportPersonnel.toolTipText=This is the maximum number of starting support personnel for the force. This doesn't include assistants. supportPersonnelNumbersPanel.title=Support Personnel Assignment supportPersonnelNumber.toolTipText=The number of %s(s) to hire. chkPoolAssistants.text=Pool Assistants -chkPoolAssistants.toolTipText=Automatically fills the unit's astech and medic pools, otherwise they are hired as permanent members of the unit. +chkPoolAssistants.toolTipText=Automatically fills the unit's astech and medic pools, otherwise they're hired as permanent members of the unit. chkGenerateCaptains.text=Generate Captains chkGenerateCaptains.toolTipText=This creates Captains for every company after the first, or for every company when using a mercenary company command lance.
They have two officer skill increases, and are assigned the rank of Captain. chkAssignCompanyCommanderFlag.text=Assign Commander Flag @@ -1243,7 +1243,7 @@ chkPayForSetup.toolTipText=Pay for the generated unit from the starting cash, to chkPayForPersonnel.text=Pay for Personnel chkPayForPersonnel.toolTipText=Pay to hire personnel, at the standard hiring rate of two-month salary. chkPayForUnits.text=Pay for Units -chkPayForUnits.toolTipText=Pay for units, at either their full purchase cost or half of it if they are a person's attached unit. +chkPayForUnits.toolTipText=Pay for units, at either their full purchase cost or half of it if they're a person's attached unit. chkPayForParts.text=Pay for Parts chkPayForParts.toolTipText=Pay for the spare parts generated, if any are generated. chkPayForArmour.text=Pay for Armour @@ -1282,7 +1282,7 @@ chkRandomizeDependentsOrigin.text=Randomize Origin for Dependents chkRandomizeDependentsOrigin.toolTipText=Dependents have their origins randomized so that they don't come from the current planet and the campaign's faction
but instead have origins randomized in the same way as standard personnel. chkRandomizeAroundSpecifiedPlanet.text=Randomize Around Specified Planet chkRandomizeAroundSpecifiedPlanet.toolTipText=This randomizes the personnel around a specified planet instead of the current planet, which allows one to have a company generated from within a specified region around that planet. -chkSpecifiedSystemFactionSpecific.toolTipText=Filter the specified planet options, so they are specific to the faction selected. +chkSpecifiedSystemFactionSpecific.toolTipText=Filter the specified planet options, so they're specific to the faction selected. lblSpecifiedPlanet.text=Specified Planet lblSpecifiedPlanet.toolTipText=This is the specified planet around which personnel are randomized. comboSpecifiedSystem.toolTipText=This is the system from which to select the specified planet around which origin planet and faction are randomized. @@ -1291,7 +1291,7 @@ lblOriginSearchRadius.toolTipText=This is the radius in light years from the cur lblOriginDistanceScale.text=Origin Distance Scale lblOriginDistanceScale.toolTipText=A scaling factor to apply to planetary distances during weighting when randomizing the faction and planetary origins.
Values above 1.0 prefer the current location, while values closer to 0.1 spread out the faction selection. chkAllowClanOrigins.text=Clan Origin Faction Generated for Non-Clan Factions -chkAllowClanOrigins.toolTipText=Generate Clan origin factions for factions that are not tagged as Clan. +chkAllowClanOrigins.toolTipText=Generate Clan origin factions for factions that aren't tagged as Clan. chkExtraRandomOrigin.text=Extra Random Planetary Origin chkExtraRandomOrigin.toolTipText=Random origin is randomized to the planetary level when selected, rather than just randomizing to the system level
(with the planet being the primary planet). ### Option Validation Warnings @@ -1309,7 +1309,7 @@ btnLoadStoryArc.text=Load a Story Arc ### Rank System rankSystemPanel.title=Rank System txtInstructionsRanks.title=Customizing Ranks -txtInstructionsRanks.text=You can use the table here to assign ranks for your campaign. You can use one of the preset rank systems from the pull-down menu, or you can design your own by creating a custom rank system. \nYou can save a single rank system as part of the campaign, with any additional custom rank systems to be saved in the user data file instead. Any additional campaign rank systems will be deleted. \nYou can also assign custom multipliers for salary. These multipliers do not need to take into account the officer multiplier which is addressed elsewhere. \n\nWARNING: \n1) This dialog does not warn about the deletion of any campaign custom rank systems that are not the selected rank system for the campaign at this time. \n2) All personnel ranks will be revalidated when this is changed, to migrate them to the proper setup for their new rank system. \n3) This dialog does not validate the data at this time, so be careful with circular logic and ensure you have a valid E0 rank (one MUST be a name like "None" or "Grunt", with "None" specifically handled in code to show as a blank string when the rank name is displayed). +txtInstructionsRanks.text=You can use the table here to assign ranks for your campaign. You can use one of the preset rank systems from the pull-down menu, or you can design your own by creating a custom rank system. \nYou can save a single rank system as part of the campaign, with any additional custom rank systems to be saved in the user data file instead. Any additional campaign rank systems will be deleted. \nYou can also assign custom multipliers for salary. These multipliers don't need to take into account the officer multiplier which is addressed elsewhere. \n\nWARNING: \n1) This dialog doesn't warn about the deletion of any campaign custom rank systems that aren't the selected rank system for the campaign at this time. \n2) All personnel ranks will be revalidated when this is changed, to migrate them to the proper setup for their new rank system. \n3) This dialog does not validate the data at this time, so be careful with circular logic and ensure you have a valid E0 rank (one MUST be a name like "None" or "Grunt", with "None" specifically handled in code to show as a blank string when the rank name is displayed). lblRankSystem.text=Rank System lblRankSystem.toolTipText=This is the standard rank system used in this campaign. comboRankSystemType.toolTipText=This is the type of rank system selected, which is where the information about the rank system is stored. @@ -1326,7 +1326,7 @@ btnExportRankSystems.toolTipText=This exports all rank systems to a specif btnImportIndividualRankSystem.text=Import Individual Rank System btnImportIndividualRankSystem.toolTipText=This imports an individual rank system from a selected file.
This is loaded as an option for ranks, and in the style of a campaign custom rank system.
This can then be stored in user data by converting the system into a user data rank system before exporting it as part of exporting the user data rank systems. btnImportRankSystems.text=Import Rank Systems -btnImportRankSystems.toolTipText=This bulk imports rank systems from a selected file. They are not saved to the user data file and will be discarded
unless they are either selected for the campaign (which saves the single selected one with the campaign)
or are individually converted into user data rank systems and then exported as part of exporting the user data rank systems. +btnImportRankSystems.toolTipText=This bulk imports rank systems from a selected file. They aren't saved to the user data file and will be discarded
unless they're either selected for the campaign (which saves the single selected one with the campaign)
or are individually converted into user data rank systems and then exported as part of exporting the user data rank systems. btnRefreshRankSystemsFromFile.text=Refresh Rank Systems From Files btnRefreshRankSystemsFromFile.toolTipText=This clears and does a complete reload of ranks from file, updating the GUI and rerunning rank validation for all personnel. diff --git a/MekHQ/resources/mekhq/resources/Personnel.properties b/MekHQ/resources/mekhq/resources/Personnel.properties index 302e843229..fb2f4e1d2a 100644 --- a/MekHQ/resources/mekhq/resources/Personnel.properties +++ b/MekHQ/resources/mekhq/resources/Personnel.properties @@ -28,6 +28,7 @@ cannotDivorce.RandomClanPersonnel.text=They are from a clan, and random clan per cannotDivorce.RandomClanPersonnelSpouse.text=Their spouse is from a clan, and random clan personnel divorce is disabled. cannotDivorce.RandomPrisoner.text=They are a prisoner, and random prisoner divorce is disabled. cannotDivorce.RandomPrisonerSpouse.text=Their spouse is a prisoner, and random prisoner divorce is disabled. +cannotDivorce.RandomNotOriginSpouse.text=Only the character that initiated a marriage can roll for divorce cannotDivorce.OppositeSexDivorceDisabled.text=They cannot randomly divorce as they are in an opposite sex marriage and opposite sex random divorce is disabled. cannotDivorce.SameSexDivorceDisabled.text=They cannot randomly divorce as they are in a same-sex marriage and same-sex random divorce is disabled. divorce.report=%s has divorced %s. @@ -354,6 +355,10 @@ PersonnelStatus.ON_LEAVE.text=On Leave PersonnelStatus.ON_LEAVE.toolTipText=They are currently on leave from the force. PersonnelStatus.ON_LEAVE.reportText=%s has gone on leave from the force PersonnelStatus.ON_LEAVE.logText=Went on leave +PersonnelStatus.ON_MATERNITY_LEAVE.text=On Maternity Leave +PersonnelStatus.ON_MATERNITY_LEAVE.toolTipText=They are currently on maternity leave. +PersonnelStatus.ON_MATERNITY_LEAVE.reportText=%s has gone on maternity leave +PersonnelStatus.ON_MATERNITY_LEAVE.logText=Went on maternity leave PersonnelStatus.AWOL.text=AWOL PersonnelStatus.AWOL.toolTipText=They have abandoned their post. PersonnelStatus.AWOL.reportText=%s has abandoned their post. @@ -514,20 +519,20 @@ RandomDependentMethod.AGAINST_THE_BOT.toolTipText=This follows the dependent add # RandomDivorceMethod RandomDivorceMethod.NONE.text=Disabled RandomDivorceMethod.NONE.toolTipText=Random divorce is disabled. -RandomDivorceMethod.PERCENTAGE.text=Percentage -RandomDivorceMethod.PERCENTAGE.toolTipText=This checks if a random value is lower than the percentage to determine if an eligible married person divorces on a given day. +RandomDivorceMethod.DICE_ROLL.text=Dice Roll +RandomDivorceMethod.DICE_ROLL.toolTipText=A die is rolled each week to determine whether a marriage ends in divorce. The options below determine how many sides this dice has. Divorce occurs on a roll of 1. # RandomMarriageMethod Enum RandomMarriageMethod.NONE.text=Disabled RandomMarriageMethod.NONE.toolTipText=Random marriage is disabled. -RandomMarriageMethod.PERCENTAGE.text=Percentage -RandomMarriageMethod.PERCENTAGE.toolTipText=This checks if a random value is lower than the percentage to determine if an eligible person marries on a given day. +RandomMarriageMethod.DICE_ROLL.text=Dice Roll +RandomMarriageMethod.DICE_ROLL.toolTipText=A die is rolled each week to determine whether an eligible character gets married to another eligible character. The options below determine how many sides this dice has. Marriage occurs on a roll of 1. # RandomProcreationMethod Enum RandomProcreationMethod.NONE.text=Disabled RandomProcreationMethod.NONE.toolTipText=Random procreation is disabled. -RandomProcreationMethod.PERCENTAGE.text=Percentage -RandomProcreationMethod.PERCENTAGE.toolTipText=This checks if a random value is lower than the percentage to determine if an eligible person procreates on a given day. +RandomProcreationMethod.DICE_ROLL.text=Dice Roll +RandomProcreationMethod.DICE_ROLL.toolTipText=Once per week roll a die with sides equal to those set below. On a roll of 1 the character falls pregnant. # TurnoverTargetNumberMethod Enum TurnoverTargetNumberMethod.FIXED.text=Fixed @@ -686,7 +691,7 @@ cannotProcreate.NotTryingForABaby.text=She is not trying for a baby. cannotProcreate.AlreadyPregnant.text=She is already pregnant. cannotProcreate.Inactive.text=She is currently inactive. cannotProcreate.Deployed.text=She is actively deployed. -cannotProcreate.Child.text=She is a child. Those thirteen and under cannot procreate in MekHQ. +cannotProcreate.Child.text=She is a child. Those 18 and under cannot procreate in MekHQ. cannotProcreate.TooOld.text=She is too old to have child. Those fifty-one and over cannot procreate in MekHQ. cannotProcreate.ClanPersonnel.text=She is from a clan, and clan personnel procreation is disabled. cannotProcreate.Prisoner.text=She is a prisoner, and prisoner procreation is disabled. diff --git a/MekHQ/src/mekhq/campaign/Campaign.java b/MekHQ/src/mekhq/campaign/Campaign.java index 122a36bd34..d1a51d7958 100644 --- a/MekHQ/src/mekhq/campaign/Campaign.java +++ b/MekHQ/src/mekhq/campaign/Campaign.java @@ -84,9 +84,7 @@ import mekhq.campaign.personnel.education.Academy; import mekhq.campaign.personnel.education.EducationController; import mekhq.campaign.personnel.enums.*; -import mekhq.campaign.personnel.generator.AbstractPersonnelGenerator; -import mekhq.campaign.personnel.generator.DefaultPersonnelGenerator; -import mekhq.campaign.personnel.generator.RandomPortraitGenerator; +import mekhq.campaign.personnel.generator.*; import mekhq.campaign.personnel.marriage.AbstractMarriage; import mekhq.campaign.personnel.marriage.DisabledRandomMarriage; import mekhq.campaign.personnel.procreation.AbstractProcreation; @@ -1439,18 +1437,24 @@ public Unit getUnit(UUID id) { // region Personnel // region Person Creation /** - * @return A new {@link Person}, who is a dependent. + * Creates a new {@link Person} instance who is a dependent. + * If {@code baby} is false and the random dependent origin option is enabled, + * the new person will have a random origin. + * + * @param baby a boolean indicating if the person is a baby or not + * @param gender the Gender enum for the person (should normally be Gender.RANDOMIZE) + * @return a new {@link Person} instance who is a dependent */ - public Person newDependent(boolean baby) { + public Person newDependent(boolean baby, Gender gender) { Person person; - if (!baby && getCampaignOptions().getRandomOriginOptions().isRandomizeDependentOrigin()) { - person = newPerson(PersonnelRole.DEPENDENT); - } else { + if ((!baby) && (getCampaignOptions().getRandomOriginOptions().isRandomizeDependentOrigin())) { person = newPerson(PersonnelRole.DEPENDENT, PersonnelRole.NONE, new DefaultFactionSelector(getCampaignOptions().getRandomOriginOptions()), new DefaultPlanetSelector(getCampaignOptions().getRandomOriginOptions()), - Gender.RANDOMIZE); + gender); + } else { + person = newPerson(PersonnelRole.DEPENDENT); } return person; @@ -1572,8 +1576,8 @@ public void setFieldKitchenWithinCapacity(final Boolean fieldKitchenWithinCapaci // region Personnel Recruitment /** - * @param p the person being added - * @return true if the person is hired successfully, otherwise false + * @param p the person being added + * @return true, if the person is hired successfully, otherwise false */ public boolean recruitPerson(Person p) { return recruitPerson(p, p.getPrisonerStatus(), false, true); @@ -1613,8 +1617,8 @@ public boolean recruitPerson(Person p, PrisonerStatus prisonerStatus, boolean gm if (p == null) { return false; } - // Only pay if option set, they weren't GM added, and they aren't a dependent, - // prisoner or bondsman + + // Only pay if option set, they weren't GM added, and they aren't a dependent, prisoner or bondsman if (getCampaignOptions().isPayForRecruitment() && !p.getPrimaryRole().isDependent() && !gmAdd && prisonerStatus.isFree()) { if (!getFinances().debit(TransactionType.RECRUITMENT, getLocalDate(), @@ -1627,6 +1631,7 @@ public boolean recruitPerson(Person p, PrisonerStatus prisonerStatus, boolean gm } personnel.put(p.getId(), p); + p.setJoinedCampaign(getLocalDate()); if (log) { String add = !prisonerStatus.isFree() ? (prisonerStatus.isBondsman() ? " as a bondsman" : " as a prisoner") @@ -1644,10 +1649,155 @@ public boolean recruitPerson(Person p, PrisonerStatus prisonerStatus, boolean gm p.setPrisonerStatus(this, prisonerStatus, log); + if (getCampaignOptions().isUseSimulatedRelationships()) { + if ((prisonerStatus.isFree()) && (!p.getOriginFaction().isClan()) && (!p.getPrimaryRole().isDependent())) { + simulateRelationshipHistory(p); + } + } + MekHQ.triggerEvent(new PersonNewEvent(p)); return true; } - // endregion Personnel Recruitment + + private void simulateRelationshipHistory(Person person) { + // how many weeks should the simulation run? + LocalDate localDate = getLocalDate(); + long weeksBetween = ChronoUnit.WEEKS.between(person.getBirthday().plusYears(18), localDate); + + // this means there is nothing to simulate + if (weeksBetween == 0) { + return; + } + + List children = new ArrayList<>(); + Person currentSpouse = null; + + // run the simulation + for (long weeksRemaining = weeksBetween; weeksRemaining >= 0; weeksRemaining--) { + LocalDate currentDate = getLocalDate().minusWeeks(weeksRemaining); + + // first, we check for old relationships ending and new relationships beginning + if (currentSpouse != null) { + getDivorce().processNewWeek(this, currentDate, person, true); + + if (!person.getGenealogy().hasSpouse()) { + List toRemove = new ArrayList<>(); + + // there is a chance a departing spouse might take some of their children with them + for (Person child : children) { + if (child.getGenealogy().getParents().contains(currentSpouse)) { + if (Compute.randomInt(2) == 0) { + toRemove.add(child); + } + } + } + + children.removeAll(toRemove); + + currentSpouse = null; + } + } else { + getMarriage().processBackgroundMarriageRolls(this, currentDate, person); + + if (person.getGenealogy().hasSpouse()) { + currentSpouse = person.getGenealogy().getSpouse(); + } + } + + // then we check for children + if (person.getGender().isFemale()) { + getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), person, true); + + if (person.isPregnant()) { + + Person father = null; + + if ((currentSpouse != null) && (currentSpouse.getGender().isMale())) { + father = currentSpouse; + } + + children.addAll(getProcreation().birthHistoric(this, person.getDueDate(), person, father)); + } + } + + if ((currentSpouse != null) && (currentSpouse.getGender().isFemale())) { + getProcreation().processRandomProcreationCheck(this, localDate.minusWeeks(weeksRemaining), person, true); + + if (person.isPregnant()) { + + Person father = null; + + if (person.getGender().isMale()) { + father = currentSpouse; + } + + getProcreation().birthHistoric(this, person.getDueDate(), person, father); + } + } + } + + // with the simulation concluded, we add the current spouse (if any) and any remaining children to the unit + if (currentSpouse != null) { + recruitPerson(currentSpouse, PrisonerStatus.FREE, true, false); + + addReport(String.format(resources.getString("relativeJoinsForce.text"), + currentSpouse.getHyperlinkedFullTitle(), + person.getHyperlinkedFullTitle(), + resources.getString("relativeJoinsForceSpouse.text"))); + + MekHQ.triggerEvent(new PersonChangedEvent(currentSpouse)); + } + + for (Person child : children) { + child.setOriginFaction(person.getOriginFaction()); + child.setOriginPlanet(person.getOriginPlanet()); + + int age = child.getAge(localDate); + + // Limit skills by age for children and adolescents + if (age < 16) { + child.removeAllSkills(); + } else if (age < 18) { + child.limitSkills(0); + } + + // re-roll SPAs to include in any age and skill adjustments + Enumeration options = new PersonnelOptions().getOptions(PersonnelOptions.LVL3_ADVANTAGES); + + for (IOption option : Collections.list(options)) { + child.getOptions().getOption(option.getName()).clearValue(); + } + + int experienceLevel = child.getExperienceLevel(this, false); + + // set loyalty + if (experienceLevel <= 0) { + person.setLoyalty(Compute.d6(3) + 2); + } else if (experienceLevel == 1) { + person.setLoyalty(Compute.d6(3) + 1); + } else { + person.setLoyalty(Compute.d6(3)); + } + + if (experienceLevel >= 0) { + AbstractSpecialAbilityGenerator specialAbilityGenerator = new DefaultSpecialAbilityGenerator(); + specialAbilityGenerator.setSkillPreferences(new RandomSkillPreferences()); + specialAbilityGenerator.generateSpecialAbilities(this, child, experienceLevel); + } + + recruitPerson(child, PrisonerStatus.FREE, true, false); + + addReport(String.format(resources.getString("relativeJoinsForce.text"), + child.getHyperlinkedFullTitle(), + person.getHyperlinkedFullTitle(), + resources.getString("relativeJoinsForceChild.text"))); + + MekHQ.triggerEvent(new PersonChangedEvent(child)); + } + + MekHQ.triggerEvent(new PersonChangedEvent(person)); + } + //endregion Personnel Recruitment // region Bloodnames /** @@ -1846,6 +1996,17 @@ public List getActivePersonnel() { .collect(Collectors.toList()); } + /** + * Provides a filtered list of personnel including only active Dependents. + * @return a {@link Person} List containing all active personnel + */ + public List getActiveDependents() { + return getPersonnel().stream() + .filter(person -> person.getPrimaryRole().isDependent()) + .filter(person -> person.getStatus().isActive()) + .collect(Collectors.toList()); + } + /** * Provides a filtered list of personnel including only active prisoners. * @@ -3561,10 +3722,11 @@ && getCampaignOptions().getRandomDependentMethod().isAgainstTheBot() } else { if (getCampaignOptions().isUseRandomDependentAddition()) { for (int i = 0; i < change; i++) { - final Person person = newDependent(false); + final Person person = newDependent(false, Gender.RANDOMIZE); recruitPerson(person, PrisonerStatus.FREE, true, false); + addReport(String.format(resources.getString("dependentJoinsForce.text"), - person.getFullTitle())); + person.getHyperlinkedFullTitle())); } } } @@ -3590,32 +3752,28 @@ && 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 - for (Person p : getActivePersonnel()) { + // This MUST use getActivePersonnel as we only want to process active personnel, and + // furthermore, this allows us to add and remove personnel without issue + for (Person person : getActivePersonnel()) { // Death - if (getDeath().processNewDay(this, getLocalDate(), p)) { + if (getDeath().processNewDay(this, getLocalDate(), person)) { // The person has died, so don't continue to process the dead continue; } - // Marriage - getMarriage().processNewDay(this, getLocalDate(), p); - - p.resetMinutesLeft(); + person.resetMinutesLeft(); // Reset acquisitions made to 0 - p.setAcquisition(0); - if (p.needsFixing() && !getCampaignOptions().isUseAdvancedMedical()) { - p.decrementDaysToWaitForHealing(); - Person doctor = getPerson(p.getDoctorId()); + person.setAcquisition(0); + if (person.needsFixing() && !getCampaignOptions().isUseAdvancedMedical()) { + person.decrementDaysToWaitForHealing(); + Person doctor = getPerson(person.getDoctorId()); if ((doctor != null) && doctor.isDoctor()) { - if (p.getDaysToWaitForHealing() <= 0) { - addReport(healPerson(p, doctor)); + if (person.getDaysToWaitForHealing() <= 0) { + addReport(healPerson(person, doctor)); } - } else if (p.checkNaturalHealing(15)) { - addReport(p.getHyperlinkedFullTitle() + " heals naturally!"); - Unit u = p.getUnit(); + } else if (person.checkNaturalHealing(15)) { + addReport(person.getHyperlinkedFullTitle() + " heals naturally!"); + Unit u = person.getUnit(); if (u != null) { u.resetPilotAndEntity(); } @@ -3623,54 +3781,75 @@ public void processNewDayPersonnel() { } // TODO Advanced Medical needs to go away from here later on if (getCampaignOptions().isUseAdvancedMedical()) { - InjuryUtil.resolveDailyHealing(this, p); - Unit u = p.getUnit(); + InjuryUtil.resolveDailyHealing(this, person); + Unit u = person.getUnit(); if (u != null) { u.resetPilotAndEntity(); } } - // TODO : Reset this based on hasSupportRole(false) instead of checking for each - // type - // TODO : p.isEngineer will need to stay, however + // TODO : Reset this based on hasSupportRole(false) instead of checking for each type + // TODO : person.isEngineer will need to stay, however // Reset edge points to the purchased value each week. This should only // apply for support personnel - combat troops reset with each new mm game - if ((p.isAdministrator() || p.isDoctor() || p.isEngineer() || p.isTech()) + if ((person.isAdministrator() || person.isDoctor() || person.isEngineer() || person.isTech()) && (getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY)) { - p.resetCurrentEdge(); + person.resetCurrentEdge(); } if ((getCampaignOptions().getIdleXP() > 0) && (getLocalDate().getDayOfMonth() == 1) - && !p.getPrisonerStatus().isCurrentPrisoner()) { // Prisoners can't gain XP, while Bondsmen can gain - // xp - p.setIdleMonths(p.getIdleMonths() + 1); - if (p.getIdleMonths() >= getCampaignOptions().getMonthsIdleXP()) { + && !person.getPrisonerStatus().isCurrentPrisoner()) { // Prisoners can't gain XP, while Bondsmen can gain xp + person.setIdleMonths(person.getIdleMonths() + 1); + if (person.getIdleMonths() >= getCampaignOptions().getMonthsIdleXP()) { if (Compute.d6(2) >= getCampaignOptions().getTargetIdleXP()) { - p.awardXP(this, getCampaignOptions().getIdleXP()); - addReport(p.getHyperlinkedFullTitle() + " has gained " + person.awardXP(this, getCampaignOptions().getIdleXP()); + addReport(person.getHyperlinkedFullTitle() + " has gained " + getCampaignOptions().getIdleXP() + " XP"); } - p.setIdleMonths(0); + person.setIdleMonths(0); } } - // Divorce - getDivorce().processNewDay(this, getLocalDate(), p); + // Divorce, Marriage, & Procreation + if (getLocalDate().getDayOfWeek() == DayOfWeek.MONDAY) { + getDivorce().processNewWeek(this, getLocalDate(), person, false); + getMarriage().processNewWeek(this, getLocalDate(), person); + getProcreation().processNewWeek(this, getLocalDate(), person); + + if (person.getGender().isFemale()) { + if (campaignOptions.isUseMaternityLeave()) { + if ((person.isPregnant()) + && (person.getStatus().isActive()) + && (person.getDueDate().minusWeeks(20).isEqual(getLocalDate()))) { + + person.changeStatus(this, getLocalDate(), PersonnelStatus.ON_MATERNITY_LEAVE); + } + + List children = person.getGenealogy().getChildren(); + + if ((person.getStatus().isOnMaternityLeave()) && (!children.isEmpty())) { - // Procreation - getProcreation().processNewDay(this, getLocalDate(), p); + children.sort(Comparator.comparing(Person::getBirthday).reversed()); + + if (getLocalDate().isAfter(children.get(0).getBirthday().plusDays(41))) { + person.changeStatus(this, getLocalDate(), PersonnelStatus.ACTIVE); + } + } + } + } + } // Anniversaries - if ((p.getRank().isOfficer()) || (!getCampaignOptions().isAnnounceOfficersOnly())) { - if ((p.getBirthday().isEqual(getLocalDate())) && (campaignOptions.isAnnounceBirthdays())) { + if ((person.getRank().isOfficer()) || (!getCampaignOptions().isAnnounceOfficersOnly())) { + if ((person.getBirthday().isEqual(getLocalDate())) && (campaignOptions.isAnnounceBirthdays())) { addReport(String.format(resources.getString("anniversaryBirthday.text"), - p.getHyperlinkedFullTitle(), - p.getAge(getLocalDate()))); + person.getHyperlinkedFullTitle(), + person.getAge(getLocalDate()))); } - } else if ((p.getAge(getLocalDate()) == 18) && (campaignOptions.isAnnounceChildBirthdays())) { - if (p.getBirthday().isEqual(getLocalDate())) { + } else if ((person.getAge(getLocalDate()) == 18) && (campaignOptions.isAnnounceChildBirthdays()) ){ + if (person.getBirthday().isEqual(getLocalDate())) { addReport(String.format(resources.getString("anniversaryBirthday.text"), - p.getHyperlinkedFullTitle(), + person.getHyperlinkedFullTitle(), 18)); } } @@ -3681,8 +3860,8 @@ public void processNewDayPersonnel() { int score = 0; - if (p.getPrimaryRole().isSupport(true)) { - int dice = p.getExperienceLevel(this, false); + if (person.getPrimaryRole().isSupport(true)) { + int dice = person.getExperienceLevel(this, false); if (dice > 0) { score = Compute.d6(dice); @@ -3691,19 +3870,19 @@ public void processNewDayPersonnel() { multiplier += 0.5; } - if (p.getSecondaryRole().isSupport(true)) { - int dice = p.getExperienceLevel(this, true); + if (person.getSecondaryRole().isSupport(true)) { + int dice = person.getExperienceLevel(this, true); if (dice > 0) { score += Compute.d6(dice); } multiplier += 0.5; - } else if (p.getSecondaryRole().isNone()) { + } else if (person.getSecondaryRole().isNone()) { multiplier += 0.5; } - p.changeAutoAwardSupportPoints((int) (score * multiplier)); + person.changeAutoAwardSupportPoints((int) (score * multiplier)); } } } @@ -3924,6 +4103,10 @@ public boolean newDay() { autoAwardsController.ManualController(this, false); } + if ((getLocation().isOnPlanet()) && (getLocalDate().getDayOfMonth() == 1)) { + processRandomDependents(); + } + resetAstechMinutes(); processNewDayUnits(); @@ -3988,6 +4171,103 @@ private void processReputationChanges() { } } + /** + * This method processes the random dependents for a campaign. It shuffles the active dependents list and performs + * actions based on the campaign options and unit rating modifiers. + * + * First, it determines the dependent capacity based on 20% of the active personnel count. Then, it calculates + * the number of dependents currently in the list. + * + * If the campaign options allow random dependent removal, it iterates over each dependent and determines if they + * should leave the force based on a lower roll value. If the roll value is less than or equal to 4 minus the unit + * rating modifier, the dependent is removed from the force. + * + * If the campaign options allow random dependent addition and the number of dependents is less than the dependent + * capacity, it iterates a number of times equal to the difference between the dependent capacity and the number of + * dependents. It determines if a lower roll value is less than or equal to the unit rating modifier multiplied by 2. + * If true, it recruits a new dependent and adds a report indicating the dependent has joined the force. + */ + private void processRandomDependents() { + List dependents = getActiveDependents(); + Collections.shuffle(dependents); + + // we use this value a lot, so might as well store it for easier retrieval + LocalDate currentDate = getLocalDate(); + + // we don't want to include Dependents or children when determining capacity + List activeNonDependents = getActivePersonnel().stream() + .filter(person -> !person.getPrimaryRole().isDependent()) + .filter(person -> !person.isChild(currentDate)) + .toList(); + + int dependentCapacity = (int) Math.max(1, (activeNonDependents.size() * 0.05)); + int dependentCount = dependents.size(); + + // roll for random removal + if (getCampaignOptions().isUseRandomDependentRemoval()) { + for (Person dependent : dependents) { + if (!isRemovalEligible(dependent, currentDate)) { + continue; + } + + int lowerRoll = (dependents.size() > dependentCapacity) ? getLowerRandomInt() : Compute.randomInt(100); + + if (lowerRoll <= 4 - getAtBUnitRatingMod()) { + addReport(String.format(resources.getString("dependentLeavesForce.text"), + dependent.getFullTitle())); + + removePerson(dependent, false); + dependentCount--; + } + } + } + + // then roll for random addition + if ((getCampaignOptions().isUseRandomDependentAddition()) && (dependentCount < dependentCapacity)) { + int availableCapacity = dependentCapacity - dependentCount; + int rollCount = (int) Math.max(1, availableCapacity * 0.2); + + for (int i = 0; i < rollCount; i++) { + int lowerRoll = (dependentCount <= (dependentCapacity / 2)) ? getLowerRandomInt() : Compute.randomInt(100); + + if (lowerRoll <= (getAtBUnitRatingMod() * 2)) { + final Person dependent = newDependent(false, Gender.RANDOMIZE); + + recruitPerson(dependent, PrisonerStatus.FREE, true, false); + + addReport(String.format(resources.getString("dependentJoinsForce.text"), + dependent.getHyperlinkedFullTitle())); + + dependentCount++; + } + } + } + } + + /** + * Returns the lower value between two random integers generated between 0 and 99 (inclusive). + * + * @return the lower random integer value + */ + private int getLowerRandomInt() { + int roll = Compute.randomInt(100); + int secondRoll = Compute.randomInt(100); + return Math.min(roll, secondRoll); + } + + /** + * Checks if a dependent is eligible for removal. + * + * @param dependent the person to check + * @param currentDate the current date + * @return {@code true} if the person is eligible for removal, {@code false} otherwise + */ + private boolean isRemovalEligible(Person dependent, LocalDate currentDate) { + return !(dependent.getGenealogy().hasNonAdultChildren(currentDate) + || dependent.getGenealogy().hasSpouse() + || dependent.isChild(currentDate)); + } + /** * This method iterates through the list of personnel and deletes the records of * those who have diff --git a/MekHQ/src/mekhq/campaign/CampaignOptions.java b/MekHQ/src/mekhq/campaign/CampaignOptions.java index 7dcb4f182a..ed8d130226 100644 --- a/MekHQ/src/mekhq/campaign/CampaignOptions.java +++ b/MekHQ/src/mekhq/campaign/CampaignOptions.java @@ -19,23 +19,13 @@ */ package mekhq.campaign; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import megamek.Version; import megamek.codeUtilities.MathUtility; import megamek.common.EquipmentType; import megamek.common.TechConstants; import megamek.common.enums.SkillLevel; import megamek.logging.MMLogger; +import mekhq.MekHQ; import mekhq.Utilities; import mekhq.campaign.enums.PlanetaryAcquisitionFactionLimit; import mekhq.campaign.finances.Money; @@ -45,10 +35,17 @@ import mekhq.campaign.market.enums.UnitMarketMethod; import mekhq.campaign.mission.enums.AtBLanceRole; import mekhq.campaign.parts.enums.PartRepairType; +import mekhq.campaign.personnel.Skills; import mekhq.campaign.personnel.enums.*; import mekhq.campaign.rating.UnitRatingMethod; import mekhq.service.mrms.MRMSOption; import mekhq.utilities.MHQXMLUtility; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.PrintWriter; +import java.util.*; +import java.util.Map.Entry; /** * @author natit @@ -62,7 +59,7 @@ public class CampaignOptions { public static final int TECH_ADVANCED = 2; public static final int TECH_EXPERIMENTAL = 3; public static final int TECH_UNOFFICIAL = 4; - // This must always be the highest tech level in order to hide parts + // This must always be the highest tech level to hide parts // that haven't been invented yet, or that are completely extinct public static final int TECH_UNKNOWN = 5; @@ -279,61 +276,15 @@ public static String getTransitUnitName(final int unit) { // region Life Paths Tab // Personnel Randomization private boolean useDylansRandomXP; // Unofficial + private int nonBinaryDiceSize; // Random Histories private RandomOriginOptions randomOriginOptions; private boolean useRandomPersonalities; private boolean useRandomPersonalityReputation; private boolean useIntelligenceXpMultiplier; + private boolean useSimulatedRelationships; - // Retirement - private boolean useRandomRetirement; - - 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 int payBonusDefaultThreshold; - - private boolean useCustomRetirementModifiers; - private boolean useFatigueModifiers; - private boolean useSkillModifiers; - private boolean useAgeModifiers; - private boolean useUnitRatingModifiers; - private boolean useFactionModifiers; - private boolean useHostileTerritoryModifiers; - private boolean useMissionStatusModifiers; - private boolean useFamilyModifiers; - 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 boolean fieldKitchenIgnoreNonCombatants; - private int fatigueLeaveThreshold; // Family private FamilialRelationshipDisplayLevel familyDisplayLevel; @@ -347,17 +298,18 @@ public static String getTransitUnitName(final int unit) { private boolean useManualMarriages; private boolean useClanPersonnelMarriages; private boolean usePrisonerMarriages; - private int minimumMarriageAge; private int checkMutualAncestorsDepth; + private int noInterestInMarriageDiceSize; private boolean logMarriageNameChanges; private Map marriageSurnameWeights; private RandomMarriageMethod randomMarriageMethod; - private boolean useRandomSameSexMarriages; + private boolean useRandomSameSexMarriages; // legacy, pre-50.01 private boolean useRandomClanPersonnelMarriages; private boolean useRandomPrisonerMarriages; private int randomMarriageAgeRange; - private double percentageRandomMarriageOppositeSexChance; - private double percentageRandomMarriageSameSexChance; + private int randomMarriageDiceSize; + private int randomSameSexMarriageDiceSize; + private int randomNewDependentMarriage; // Divorce private boolean useManualDivorce; @@ -369,8 +321,7 @@ public static String getTransitUnitName(final int unit) { private boolean useRandomSameSexDivorce; private boolean useRandomClanPersonnelDivorce; private boolean useRandomPrisonerDivorce; - private double percentageRandomDivorceOppositeSexChance; - private double percentageRandomDivorceSameSexChance; + private int randomDivorceDiceSize; // Procreation private boolean useManualProcreation; @@ -380,15 +331,17 @@ public static String getTransitUnitName(final int unit) { private BabySurnameStyle babySurnameStyle; private boolean assignNonPrisonerBabiesFounderTag; private boolean assignChildrenOfFoundersFounderTag; + private boolean useMaternityLeave; private boolean determineFatherAtBirth; private boolean displayTrueDueDate; + private int noInterestInChildrenDiceSize; private boolean logProcreation; private RandomProcreationMethod randomProcreationMethod; private boolean useRelationshiplessRandomProcreation; private boolean useRandomClanPersonnelProcreation; private boolean useRandomPrisonerProcreation; - private double percentageRandomProcreationRelationshipChance; - private double percentageRandomProcreationRelationshiplessChance; + private int randomProcreationRelationshipDiceSize; + private int randomProcreationRelationshiplessDiceSize; // Education private boolean useEducationModule; @@ -422,7 +375,57 @@ public static String getTransitUnitName(final int unit) { private Map ageRangeRandomDeathFemaleValues; // endregion Life Paths Tab - // region Finance tab + //region Turnover and Retention + private boolean useRandomRetirement; + + 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 int payBonusDefaultThreshold; + + private boolean useCustomRetirementModifiers; + private boolean useFatigueModifiers; + private boolean useSkillModifiers; + private boolean useAgeModifiers; + private boolean useUnitRatingModifiers; + private boolean useFactionModifiers; + private boolean useHostileTerritoryModifiers; + private boolean useMissionStatusModifiers; + private boolean useFamilyModifiers; + 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 boolean fieldKitchenIgnoreNonCombatants; + private int fatigueLeaveThreshold; + //endregion Turnover and Retention + + //region Finance tab private boolean payForParts; private boolean payForRepairs; private boolean payForUnits; @@ -839,12 +842,14 @@ public CampaignOptions() { // region Life Paths Tab // Personnel Randomization setUseDylansRandomXP(false); + setNonBinaryDiceSize(60); // Random Histories setRandomOriginOptions(new RandomOriginOptions(true)); setUseRandomPersonalities(false); setUseRandomPersonalityReputation(true); setUseIntelligenceXpMultiplier(true); + setUseSimulatedRelationships(false); // Family setFamilyDisplayLevel(FamilialRelationshipDisplayLevel.SPOUSE); @@ -858,8 +863,8 @@ public CampaignOptions() { setUseManualMarriages(true); setUseClanPersonnelMarriages(false); setUsePrisonerMarriages(true); - setMinimumMarriageAge(16); setCheckMutualAncestorsDepth(4); + setNoInterestInMarriageDiceSize(10); setLogMarriageNameChanges(false); setMarriageSurnameWeights(new HashMap<>()); getMarriageSurnameWeights().put(MergingSurnameStyle.NO_CHANGE, 100); @@ -876,12 +881,12 @@ public CampaignOptions() { getMarriageSurnameWeights().put(MergingSurnameStyle.MALE, 500); getMarriageSurnameWeights().put(MergingSurnameStyle.FEMALE, 160); setRandomMarriageMethod(RandomMarriageMethod.NONE); - setUseRandomSameSexMarriages(false); setUseRandomClanPersonnelMarriages(false); - setUseRandomPrisonerMarriages(true); + setUseRandomPrisonerMarriages(false); setRandomMarriageAgeRange(10); - setPercentageRandomMarriageOppositeSexChance(0.00025); - setPercentageRandomMarriageSameSexChance(0.00002); + setRandomMarriageDiceSize(5000); + setRandomSameSexMarriageDiceSize(14); + setRandomNewDependentMarriage(20); // Divorce setUseManualDivorce(true); @@ -897,26 +902,27 @@ public CampaignOptions() { setUseRandomSameSexDivorce(true); setUseRandomClanPersonnelDivorce(true); setUseRandomPrisonerDivorce(false); - setPercentageRandomDivorceOppositeSexChance(0.000001); - setPercentageRandomDivorceSameSexChance(0.000001); + setRandomDivorceDiceSize(900); // Procreation setUseManualProcreation(true); setUseClanPersonnelProcreation(false); setUsePrisonerProcreation(true); - setMultiplePregnancyOccurrences(50); // Hellin's Law is 89, but we make it more common so it shows up more + setMultiplePregnancyOccurrences(50); // Hellin's Law is 89, but we make it more common, so it shows up more setBabySurnameStyle(BabySurnameStyle.MOTHERS); setAssignNonPrisonerBabiesFounderTag(false); setAssignChildrenOfFoundersFounderTag(false); + setUseMaternityLeave(true); setDetermineFatherAtBirth(false); setDisplayTrueDueDate(false); + setNoInterestInChildrenDiceSize(3); setLogProcreation(false); setRandomProcreationMethod(RandomProcreationMethod.NONE); setUseRelationshiplessRandomProcreation(false); setUseRandomClanPersonnelProcreation(false); setUseRandomPrisonerProcreation(true); - setPercentageRandomProcreationRelationshipChance(0.0005); - setPercentageRandomProcreationRelationshiplessChance(0.00005); + setRandomProcreationRelationshipDiceSize(500); + setRandomProcreationRelationshiplessDiceSize(2000); // Education setUseEducationModule(false); @@ -1627,7 +1633,7 @@ public void setFatigueLeaveThreshold(final Integer fatigueLeaveThreshold) { // region Expanded Personnel Information /** - * @return whether or not to use time in service + * @return whether to use time in service */ public boolean isUseTimeInService() { return useTimeInService; @@ -1656,14 +1662,14 @@ public void setTimeInServiceDisplayFormat(final TimeInDisplayFormat timeInServic } /** - * @return whether or not to use time in rank + * @return whether to use time in rank */ public boolean isUseTimeInRank() { return useTimeInRank; } /** - * @param useTimeInRank the new value for whether or not to use time in rank + * @param useTimeInRank the new value for whether to use time in rank */ public void setUseTimeInRank(final boolean useTimeInRank) { this.useTimeInRank = useTimeInRank; @@ -1684,30 +1690,28 @@ public void setTimeInRankDisplayFormat(final TimeInDisplayFormat timeInRankDispl } /** - * @return whether or not to track the total earnings of personnel + * @return whether to track the total earnings of personnel */ public boolean isTrackTotalEarnings() { return trackTotalEarnings; } /** - * @param trackTotalEarnings the new value for whether or not to track total - * earnings for personnel + * @param trackTotalEarnings the new value for whether to track total earnings for personnel */ public void setTrackTotalEarnings(final boolean trackTotalEarnings) { this.trackTotalEarnings = trackTotalEarnings; } /** - * @return whether or not to track the total experience earnings of personnel + * @return whether to track the total experience earnings of personnel */ public boolean isTrackTotalXPEarnings() { return trackTotalXPEarnings; } /** - * @param trackTotalXPEarnings the new value for whether or not to track total - * experience + * @param trackTotalXPEarnings the new value for whether to track total experience * earnings for personnel */ public void setTrackTotalXPEarnings(final boolean trackTotalXPEarnings) { @@ -1715,8 +1719,7 @@ public void setTrackTotalXPEarnings(final boolean trackTotalXPEarnings) { } /** - * Gets a value indicating whether or not to show a person's origin faction when - * displaying + * Gets a value indicating whether to show a person's origin faction when displaying * their details. */ public boolean isShowOriginFaction() { @@ -1724,8 +1727,7 @@ public boolean isShowOriginFaction() { } /** - * Sets a value indicating whether or not to show a person's origin faction when - * displaying + * Sets a value indicating whether to show a person's origin faction when displaying * their details. */ public void setShowOriginFaction(final boolean showOriginFaction) { @@ -1873,7 +1875,16 @@ public boolean isUseDylansRandomXP() { public void setUseDylansRandomXP(final boolean useDylansRandomXP) { this.useDylansRandomXP = useDylansRandomXP; } + public int getNonBinaryDiceSize() { + return nonBinaryDiceSize; + } + + public void setNonBinaryDiceSize(final int nonBinaryDiceSize) { + this.nonBinaryDiceSize = nonBinaryDiceSize; + } + //endregion Personnel Randomization + //region Random Histories public RandomOriginOptions getRandomOriginOptions() { return randomOriginOptions; } @@ -1905,7 +1916,15 @@ public boolean isUseIntelligenceXpMultiplier() { public void setUseIntelligenceXpMultiplier(final boolean useIntelligenceXpMultiplier) { this.useIntelligenceXpMultiplier = useIntelligenceXpMultiplier; } - // endregion Personnel Randomization + + public boolean isUseSimulatedRelationships() { + return useSimulatedRelationships; + } + + public void setUseSimulatedRelationships(final boolean useSimulatedRelationships) { + this.useSimulatedRelationships = useSimulatedRelationships; + } + //endregion Random Histories // region Retirement public boolean isUseRandomRetirement() { @@ -2319,14 +2338,14 @@ public void setRoleBaseSalary(final PersonnelRole role, final Money baseSalary) // region Marriage /** - * @return whether or not to use manual marriages + * @return whether to use manual marriages */ public boolean isUseManualMarriages() { return useManualMarriages; } /** - * @param useManualMarriages whether or not to use manual marriages + * @param useManualMarriages whether to use manual marriages */ public void setUseManualMarriages(final boolean useManualMarriages) { this.useManualMarriages = useManualMarriages; @@ -2348,20 +2367,6 @@ public void setUsePrisonerMarriages(final boolean usePrisonerMarriages) { this.usePrisonerMarriages = usePrisonerMarriages; } - /** - * @return the minimum age a person can get married at - */ - public int getMinimumMarriageAge() { - return minimumMarriageAge; - } - - /** - * @param minimumMarriageAge the minimum age a person can get married at - */ - public void setMinimumMarriageAge(final int minimumMarriageAge) { - this.minimumMarriageAge = minimumMarriageAge; - } - /** * This gets the number of recursions to use when checking mutual ancestors * between two personnel @@ -2382,8 +2387,16 @@ public void setCheckMutualAncestorsDepth(final int checkMutualAncestorsDepth) { this.checkMutualAncestorsDepth = checkMutualAncestorsDepth; } + public int getNoInterestInMarriageDiceSize() { + return noInterestInMarriageDiceSize; + } + + public void setNoInterestInMarriageDiceSize(final int noInterestInMarriageDiceSize) { + this.noInterestInMarriageDiceSize = noInterestInMarriageDiceSize; + } + /** - * @return whether or not to log a name change in a marriage + * @return whether to log a name change in a marriage */ public boolean isLogMarriageNameChanges() { return logMarriageNameChanges; @@ -2420,16 +2433,17 @@ public void setRandomMarriageMethod(final RandomMarriageMethod randomMarriageMet } /** - * @return whether or not to use random same-sex marriages + * @return whether to use random same-sex marriages */ + @Deprecated public boolean isUseRandomSameSexMarriages() { return useRandomSameSexMarriages; } /** - * @param useRandomSameSexMarriages whether or not to use random same-sex - * marriages + * @param useRandomSameSexMarriages whether to use random same-sex marriages */ + @Deprecated public void setUseRandomSameSexMarriages(final boolean useRandomSameSexMarriages) { this.useRandomSameSexMarriages = useRandomSameSexMarriages; } @@ -2471,45 +2485,51 @@ public void setRandomMarriageAgeRange(final int randomMarriageAgeRange) { } /** - * This gets the decimal chance (between 0 and 1) of a random opposite sex - * marriage occurring - * - * @return the chance, with a value between 0 and 1 + * @return the number of sides on the die used to determine random marriage */ - public double getPercentageRandomMarriageOppositeSexChance() { - return percentageRandomMarriageOppositeSexChance; + public int getRandomMarriageDiceSize() { + return randomMarriageDiceSize; } /** - * This sets the decimal chance (between 0 and 1) of a random opposite sex - * marriage occurring + * Sets the size of the random marriage die. * - * @param percentageRandomMarriageOppositeSexChance the chance, with a value - * between 0 and 1 + * @param randomMarriageDiceSize the size of the random marriage die */ - public void setPercentageRandomMarriageOppositeSexChance(final double percentageRandomMarriageOppositeSexChance) { - this.percentageRandomMarriageOppositeSexChance = percentageRandomMarriageOppositeSexChance; + public void setRandomMarriageDiceSize(final int randomMarriageDiceSize) { + this.randomMarriageDiceSize = randomMarriageDiceSize; } /** - * This gets the decimal chance (between 0 and 1) of a random same-sex marriage - * occurring + * @return the number of sides on the die used to determine random same-sex marriage + */ + public int getRandomSameSexMarriageDiceSize() { + return randomSameSexMarriageDiceSize; + } + + /** + * Sets the size of the random same-sex marriage die. * - * @return the chance, with a value between 0 and 1 + * @param randomSameSexMarriageDiceSize the size of the random same-sex marriage die */ - public double getPercentageRandomMarriageSameSexChance() { - return percentageRandomMarriageSameSexChance; + public void setRandomSameSexMarriageDiceSize(final int randomSameSexMarriageDiceSize) { + this.randomSameSexMarriageDiceSize = randomSameSexMarriageDiceSize; } /** - * This sets the decimal chance (between 0 and 1) of a random same-sex marriage - * occurring + * @return the number of sides on the die used to determine whether marriage occurs outside of current personnel + */ + public int getRandomNewDependentMarriage() { + return randomNewDependentMarriage; + } + + /** + * Sets the size of the die used to determine whether marriage occurs outside of current personnel * - * @param percentageRandomMarriageSameSexChance the chance, with a value between - * 0 and 1 + * @param randomNewDependentMarriage the size of the die used to determine whether marriage occurs outside of current personnel */ - public void setPercentageRandomMarriageSameSexChance(final double percentageRandomMarriageSameSexChance) { - this.percentageRandomMarriageSameSexChance = percentageRandomMarriageSameSexChance; + public void setRandomNewDependentMarriage(final int randomNewDependentMarriage) { + this.randomNewDependentMarriage = randomNewDependentMarriage; } // endregion Marriage @@ -2586,20 +2606,12 @@ public void setUseRandomPrisonerDivorce(final boolean useRandomPrisonerDivorce) this.useRandomPrisonerDivorce = useRandomPrisonerDivorce; } - public double getPercentageRandomDivorceOppositeSexChance() { - return percentageRandomDivorceOppositeSexChance; - } - - public void setPercentageRandomDivorceOppositeSexChance(final double percentageRandomDivorceOppositeSexChance) { - this.percentageRandomDivorceOppositeSexChance = percentageRandomDivorceOppositeSexChance; - } - - public double getPercentageRandomDivorceSameSexChance() { - return percentageRandomDivorceSameSexChance; + public int getRandomDivorceDiceSize() { + return randomDivorceDiceSize; } - public void setPercentageRandomDivorceSameSexChance(final double percentageRandomDivorceSameSexChance) { - this.percentageRandomDivorceSameSexChance = percentageRandomDivorceSameSexChance; + public void setRandomDivorceDiceSize(final int randomDivorceDiceSize) { + this.randomDivorceDiceSize = randomDivorceDiceSize; } // endregion Divorce @@ -2629,18 +2641,15 @@ public void setUsePrisonerProcreation(final boolean usePrisonerProcreation) { } /** - * @return the X occurrences for there to be a single multiple child occurrence - * (i.e. 1 in X) + * @return the X occurrences for there to be a single multiple child occurrence (i.e., 1 in X) */ public int getMultiplePregnancyOccurrences() { return multiplePregnancyOccurrences; } /** - * @param multiplePregnancyOccurrences the number of occurrences for there to be - * a single - * occurrence of a multiple child pregnancy - * (i.e. 1 in X) + * @param multiplePregnancyOccurrences the number of occurrences for there to be a single + * occurrence of a multiple child pregnancy (i.e., 1 in X) */ public void setMultiplePregnancyOccurrences(final int multiplePregnancyOccurrences) { this.multiplePregnancyOccurrences = multiplePregnancyOccurrences; @@ -2676,17 +2685,23 @@ public void setAssignChildrenOfFoundersFounderTag(final boolean assignChildrenOf this.assignChildrenOfFoundersFounderTag = assignChildrenOfFoundersFounderTag; } + public boolean isUseMaternityLeave() { + return useMaternityLeave; + } + + public void setUseMaternityLeave(final boolean useMaternityLeave) { + this.useMaternityLeave = useMaternityLeave; + } + /** - * @return whether or not to determine the father at birth instead of at - * conception + * @return whether to determine the father at birth instead of at conception */ public boolean isDetermineFatherAtBirth() { return determineFatherAtBirth; } /** - * @param determineFatherAtBirth whether or not to determine the father at birth - * instead of at conception + * @param determineFatherAtBirth whether to determine the father at birth instead of at conception */ public void setDetermineFatherAtBirth(final boolean determineFatherAtBirth) { this.determineFatherAtBirth = determineFatherAtBirth; @@ -2707,6 +2722,14 @@ public void setDisplayTrueDueDate(final boolean displayTrueDueDate) { this.displayTrueDueDate = displayTrueDueDate; } + public int getNoInterestInChildrenDiceSize() { + return noInterestInChildrenDiceSize; + } + + public void setNoInterestInChildrenDiceSize(final int noInterestInChildrenDiceSize) { + this.noInterestInChildrenDiceSize = noInterestInChildrenDiceSize; + } + /** * @return whether to log procreation */ @@ -2730,16 +2753,14 @@ public void setRandomProcreationMethod(final RandomProcreationMethod randomProcr } /** - * @return whether or not to use random procreation for personnel without a - * spouse + * @return whether to use random procreation for personnel without a spouse */ public boolean isUseRelationshiplessRandomProcreation() { return useRelationshiplessRandomProcreation; } /** - * @param useRelationshiplessRandomProcreation whether or not to use random - * procreation without a spouse + * @param useRelationshiplessRandomProcreation whether to use random procreation without a spouse */ public void setUseRelationshiplessRandomProcreation(final boolean useRelationshiplessRandomProcreation) { this.useRelationshiplessRandomProcreation = useRelationshiplessRandomProcreation; @@ -2767,43 +2788,31 @@ public void setUseRandomPrisonerProcreation(final boolean useRandomPrisonerProcr * * @return the chance, with a value between 0 and 1 */ - public double getPercentageRandomProcreationRelationshipChance() { - return percentageRandomProcreationRelationshipChance; + public int getRandomProcreationRelationshipDiceSize() { + return randomProcreationRelationshipDiceSize; } /** - * This sets the decimal chance (between 0 and 1) of random procreation - * occurring - * - * @param percentageRandomProcreationRelationshipChance the chance, with a value - * between 0 and 1 + * This sets the dice size for random procreation + * @param randomProcreationRelationshipDiceSize the chance, with a value between 0 and 1 */ - public void setPercentageRandomProcreationRelationshipChance( - final double percentageRandomProcreationRelationshipChance) { - this.percentageRandomProcreationRelationshipChance = percentageRandomProcreationRelationshipChance; + public void setRandomProcreationRelationshipDiceSize(final int randomProcreationRelationshipDiceSize) { + this.randomProcreationRelationshipDiceSize = randomProcreationRelationshipDiceSize; } /** - * This gets the decimal chance (between 0 and 1) of random procreation - * occurring without a relationship - * - * @return the chance, with a value between 0 and 1 + * @return the dice size for random procreation */ - public double getPercentageRandomProcreationRelationshiplessChance() { - return percentageRandomProcreationRelationshiplessChance; + public int getRandomProcreationRelationshiplessDiceSize() { + return randomProcreationRelationshiplessDiceSize; } /** - * This sets the decimal chance (between 0 and 1) of random procreation - * occurring without a relationship - * - * @param percentageRandomProcreationRelationshiplessChance the chance, with a - * value between 0 and - * 1 + * This sets the decimal chance (between 0 and 1) of random procreation occurring without a relationship + * @param randomProcreationRelationshiplessDiceSize the chance, with a value between 0 and 1 */ - public void setPercentageRandomProcreationRelationshiplessChance( - final double percentageRandomProcreationRelationshiplessChance) { - this.percentageRandomProcreationRelationshiplessChance = percentageRandomProcreationRelationshiplessChance; + public void setRandomProcreationRelationshiplessDiceSize(final int randomProcreationRelationshiplessDiceSize) { + this.randomProcreationRelationshiplessDiceSize = randomProcreationRelationshiplessDiceSize; } // endregion Procreation @@ -3337,17 +3346,14 @@ public void setFinancialYearDuration(final FinancialYearDuration financialYearDu } /** - * @return whether or not to export finances to CSV at the end of a financial - * year + * @return whether to export finances to CSV at the end of a financial year */ public boolean isNewFinancialYearFinancesToCSVExport() { return newFinancialYearFinancesToCSVExport; } /** - * @param newFinancialYearFinancesToCSVExport whether or not to export finances - * to CSV at the end of a financial - * year + * @param newFinancialYearFinancesToCSVExport whether to export finances to CSV at the end of a financial year */ public void setNewFinancialYearFinancesToCSVExport(final boolean newFinancialYearFinancesToCSVExport) { this.newFinancialYearFinancesToCSVExport = newFinancialYearFinancesToCSVExport; @@ -4806,12 +4812,17 @@ public void writeToXml(final PrintWriter pw, int indent) { // region Life Paths Tab // region Personnel Randomization MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useDylansRandomXP", isUseDylansRandomXP()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "nonBinaryDiceSize", getNonBinaryDiceSize()); + //endregion Personnel Randomization + + //region Random Histories getRandomOriginOptions().writeToXML(pw, indent); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomPersonalities", isUseRandomPersonalities()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomPersonalityReputation", isUseRandomPersonalityReputation()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useIntelligenceXpMultiplier", isUseIntelligenceXpMultiplier()); - // endregion Personnel Randomization + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useSimulatedRelationships", isUseSimulatedRelationships()); + //endregion Random Histories // region Retirement MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomRetirement", isUseRandomRetirement()); @@ -4879,8 +4890,8 @@ public void writeToXml(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useManualMarriages", isUseManualMarriages()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useClanPersonnelMarriages", isUseClanPersonnelMarriages()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "usePrisonerMarriages", isUsePrisonerMarriages()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "minimumMarriageAge", getMinimumMarriageAge()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "checkMutualAncestorsDepth", getCheckMutualAncestorsDepth()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "noInterestInMarriageDiceSize", getNoInterestInMarriageDiceSize()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "logMarriageNameChanges", isLogMarriageNameChanges()); MHQXMLUtility.writeSimpleXMLOpenTag(pw, indent++, "marriageSurnameWeights"); for (final Entry entry : getMarriageSurnameWeights().entrySet()) { @@ -4888,16 +4899,13 @@ public void writeToXml(final PrintWriter pw, int indent) { } MHQXMLUtility.writeSimpleXMLCloseTag(pw, --indent, "marriageSurnameWeights"); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomMarriageMethod", getRandomMarriageMethod().name()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomSameSexMarriages", isUseRandomSameSexMarriages()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomClanPersonnelMarriages", - isUseRandomClanPersonnelMarriages()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomClanPersonnelMarriages", isUseRandomClanPersonnelMarriages()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomPrisonerMarriages", isUseRandomPrisonerMarriages()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomMarriageAgeRange", getRandomMarriageAgeRange()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "percentageRandomMarriageOppositeSexChance", - getPercentageRandomMarriageOppositeSexChance()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "percentageRandomMarriageSameSexChance", - getPercentageRandomMarriageSameSexChance()); - // endregion Marriage + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomMarriageDiceSize", getRandomMarriageDiceSize()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomSameSexMarriageDiceSize", getRandomSameSexMarriageDiceSize()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomNewDependentMarriage", getRandomNewDependentMarriage()); + //endregion Marriage // region Divorce MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useManualDivorce", isUseManualDivorce()); @@ -4913,11 +4921,8 @@ public void writeToXml(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomSameSexDivorce", isUseRandomSameSexDivorce()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomClanPersonnelDivorce", isUseRandomClanPersonnelDivorce()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomPrisonerDivorce", isUseRandomPrisonerDivorce()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "percentageRandomDivorceOppositeSexChance", - getPercentageRandomDivorceOppositeSexChance()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "percentageRandomDivorceSameSexChance", - getPercentageRandomDivorceSameSexChance()); - // endregion Divorce + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomDivorceDiceSize", getRandomDivorceDiceSize()); + //endregion Divorce // region Procreation MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useManualProcreation", isUseManualProcreation()); @@ -4925,12 +4930,12 @@ public void writeToXml(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "usePrisonerProcreation", isUsePrisonerProcreation()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "multiplePregnancyOccurrences", getMultiplePregnancyOccurrences()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "babySurnameStyle", getBabySurnameStyle().name()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "assignNonPrisonerBabiesFounderTag", - isAssignNonPrisonerBabiesFounderTag()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "assignChildrenOfFoundersFounderTag", - isAssignChildrenOfFoundersFounderTag()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "assignNonPrisonerBabiesFounderTag", isAssignNonPrisonerBabiesFounderTag()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "assignChildrenOfFoundersFounderTag", isAssignChildrenOfFoundersFounderTag()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useMaternityLeave", isUseMaternityLeave()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "determineFatherAtBirth", isDetermineFatherAtBirth()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "displayTrueDueDate", isDisplayTrueDueDate()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "noInterestInChildrenDiceSize", getNoInterestInChildrenDiceSize()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "logProcreation", isLogProcreation()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomProcreationMethod", getRandomProcreationMethod().name()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRelationshiplessRandomProcreation", @@ -4938,11 +4943,9 @@ public void writeToXml(final PrintWriter pw, int indent) { MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomClanPersonnelProcreation", isUseRandomClanPersonnelProcreation()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useRandomPrisonerProcreation", isUseRandomPrisonerProcreation()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "percentageRandomProcreationRelationshipChance", - getPercentageRandomProcreationRelationshipChance()); - MHQXMLUtility.writeSimpleXMLTag(pw, indent, "percentageRandomProcreationRelationshiplessChance", - getPercentageRandomProcreationRelationshiplessChance()); - // endregion Procreation + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomProcreationRelationshipDiceSize", getRandomProcreationRelationshipDiceSize()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "randomProcreationRelationshiplessDiceSize", getRandomProcreationRelationshiplessDiceSize()); + //endregion Procreation // region Education MHQXMLUtility.writeSimpleXMLTag(pw, indent, "useEducationModule", isUseEducationModule()); @@ -5444,7 +5447,7 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } else if (wn2.getNodeName().equalsIgnoreCase("prisonerCaptureStyle")) { retVal.setPrisonerCaptureStyle(PrisonerCaptureStyle.valueOf(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("defaultPrisonerStatus")) { - // Most of this is legacy - 0.47.X Removal + // Most of this is legacy handlers - 0.47.X Removal String prisonerStatus = wn2.getTextContent().trim(); try { @@ -5551,6 +5554,11 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve // region Personnel Randomization } else if (wn2.getNodeName().equalsIgnoreCase("useDylansRandomXP")) { retVal.setUseDylansRandomXP(Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("nonBinaryDiceSize")) { + retVal.setNonBinaryDiceSize(Integer.parseInt(wn2.getTextContent().trim())); + //endregion Personnel Randomization + + //region Random Histories } else if (wn2.getNodeName().equalsIgnoreCase("randomOriginOptions")) { if (!wn2.hasChildNodes()) { continue; @@ -5567,7 +5575,9 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setUseRandomPersonalityReputation(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("useIntelligenceXpMultiplier")) { retVal.setUseIntelligenceXpMultiplier(Boolean.parseBoolean(wn2.getTextContent().trim())); - // endregion Personnel Randomization + } else if (wn2.getNodeName().equalsIgnoreCase("useSimulatedRelationships")) { + retVal.setUseSimulatedRelationships(Boolean.parseBoolean(wn2.getTextContent().trim())); + //endregion Random Histories // region Family } else if (wn2.getNodeName().equalsIgnoreCase("familyDisplayLevel")) { @@ -5591,10 +5601,10 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setUseClanPersonnelMarriages(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("usePrisonerMarriages")) { retVal.setUsePrisonerMarriages(Boolean.parseBoolean(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("minimumMarriageAge")) { - retVal.setMinimumMarriageAge(Integer.parseInt(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("checkMutualAncestorsDepth")) { retVal.setCheckMutualAncestorsDepth(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("noInterestInMarriageDiceSize")) { + retVal.setNoInterestInMarriageDiceSize(Integer.parseInt(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("logMarriageNameChanges")) { retVal.setLogMarriageNameChanges(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("marriageSurnameWeights")) { @@ -5613,20 +5623,24 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve } } else if (wn2.getNodeName().equalsIgnoreCase("randomMarriageMethod")) { retVal.setRandomMarriageMethod(RandomMarriageMethod.valueOf(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("useRandomSameSexMarriages")) { - retVal.setUseRandomSameSexMarriages(Boolean.parseBoolean(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("useRandomClanPersonnelMarriages")) { + } else if (wn2.getNodeName().equalsIgnoreCase("useRandomClanPersonnelMarriages") + || wn2.getNodeName().equalsIgnoreCase("useRandomClannerMarriages")) { // Legacy, 0.49.12 removal retVal.setUseRandomClanPersonnelMarriages(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("useRandomPrisonerMarriages")) { retVal.setUseRandomPrisonerMarriages(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("randomMarriageAgeRange")) { retVal.setRandomMarriageAgeRange(Integer.parseInt(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("percentageRandomMarriageOppositeSexChance")) { - retVal.setPercentageRandomMarriageOppositeSexChance( - Double.parseDouble(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("percentageRandomMarriageSameSexChance")) { - retVal.setPercentageRandomMarriageSameSexChance(Double.parseDouble(wn2.getTextContent().trim())); - // endregion Marriage + } else if (wn2.getNodeName().equalsIgnoreCase("randomMarriageDiceSize")) { + retVal.setRandomMarriageDiceSize(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("randomSameSexMarriageDiceSize")) { + retVal.setRandomSameSexMarriageDiceSize(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("useRandomSameSexMarriages")) { // Legacy, pre-50.01 + if (!Boolean.parseBoolean(wn2.getTextContent().trim())) { + retVal.setRandomSameSexMarriageDiceSize(0); + } + } else if (wn2.getNodeName().equalsIgnoreCase("randomNewDependentMarriage")) { + retVal.setRandomNewDependentMarriage(Integer.parseInt(wn2.getTextContent().trim())); + //endregion Marriage // region Divorce } else if (wn2.getNodeName().equalsIgnoreCase("useManualDivorce")) { @@ -5659,11 +5673,9 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setUseRandomClanPersonnelDivorce(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("useRandomPrisonerDivorce")) { retVal.setUseRandomPrisonerDivorce(Boolean.parseBoolean(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("percentageRandomDivorceOppositeSexChance")) { - retVal.setPercentageRandomDivorceOppositeSexChance(Double.parseDouble(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("percentageRandomDivorceSameSexChance")) { - retVal.setPercentageRandomDivorceSameSexChance(Double.parseDouble(wn2.getTextContent().trim())); - // endregion Divorce + } else if (wn2.getNodeName().equalsIgnoreCase("randomDivorceDiceSize")) { + retVal.setRandomDivorceDiceSize(Integer.parseInt(wn2.getTextContent().trim())); + //endregion Divorce // region Procreation } else if (wn2.getNodeName().equalsIgnoreCase("useManualProcreation")) { @@ -5680,10 +5692,14 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setAssignNonPrisonerBabiesFounderTag(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("assignChildrenOfFoundersFounderTag")) { retVal.setAssignChildrenOfFoundersFounderTag(Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("useMaternityLeave")) { + retVal.setUseMaternityLeave(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("determineFatherAtBirth")) { retVal.setDetermineFatherAtBirth(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("displayTrueDueDate")) { retVal.setDisplayTrueDueDate(Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("noInterestInChildrenDiceSize")) { + retVal.setNoInterestInChildrenDiceSize(Integer.parseInt(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("logProcreation")) { retVal.setLogProcreation(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("randomProcreationMethod")) { @@ -5694,13 +5710,11 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setUseRandomClanPersonnelProcreation(Boolean.parseBoolean(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("useRandomPrisonerProcreation")) { retVal.setUseRandomPrisonerProcreation(Boolean.parseBoolean(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("percentageRandomProcreationRelationshipChance")) { - retVal.setPercentageRandomProcreationRelationshipChance( - Double.parseDouble(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("percentageRandomProcreationRelationshiplessChance")) { - retVal.setPercentageRandomProcreationRelationshiplessChance( - Double.parseDouble(wn2.getTextContent().trim())); - // endregion Procreation + } else if (wn2.getNodeName().equalsIgnoreCase("randomProcreationRelationshipDiceSize")) { + retVal.setRandomProcreationRelationshipDiceSize(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("randomProcreationRelationshiplessDiceSize")) { + retVal.setRandomProcreationRelationshiplessDiceSize(Integer.parseInt(wn2.getTextContent().trim())); + //endregion Procreation // region Education } else if (wn2.getNodeName().equalsIgnoreCase("useEducationModule")) { @@ -6147,8 +6161,126 @@ public static CampaignOptions generateCampaignOptionsFromXml(Node wn, Version ve retVal.setScenarioModChance(Integer.parseInt(wn2.getTextContent().trim())); } else if (wn2.getNodeName().equalsIgnoreCase("scenarioModBV")) { retVal.setScenarioModBV(Integer.parseInt(wn2.getTextContent().trim())); - } else if (wn2.getNodeName().equalsIgnoreCase("autoConfigMunitions")) { + } else if (wn2.getNodeName().equalsIgnoreCase("autoconfigMunitions")) { retVal.setAutoConfigMunitions(Boolean.parseBoolean(wn2.getTextContent().trim())); + + //region Legacy + // Removed in 0.49.* + } else if (wn2.getNodeName().equalsIgnoreCase("salaryXPMultiplier")) { // Legacy, 0.49.12 removal + String[] values = wn2.getTextContent().split(","); + for (int i = 0; i < values.length; i++) { + retVal.getSalaryXPMultipliers().put(Skills.SKILL_LEVELS[i + 1], Double.parseDouble(values[i])); + } + } else if (wn2.getNodeName().equalsIgnoreCase("personnelMarketRandomEliteRemoval")) { // Legacy, 0.49.12 removal + retVal.getPersonnelMarketRandomRemovalTargets().put(SkillLevel.ELITE, Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("personnelMarketRandomVeteranRemoval")) { // Legacy, 0.49.12 removal + retVal.getPersonnelMarketRandomRemovalTargets().put(SkillLevel.VETERAN, Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("personnelMarketRandomRegularRemoval")) { // Legacy, 0.49.12 removal + retVal.getPersonnelMarketRandomRemovalTargets().put(SkillLevel.REGULAR, Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("personnelMarketRandomGreenRemoval")) { // Legacy, 0.49.12 removal + retVal.getPersonnelMarketRandomRemovalTargets().put(SkillLevel.GREEN, Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("personnelMarketRandomUltraGreenRemoval")) { // Legacy, 0.49.12 removal + retVal.getPersonnelMarketRandomRemovalTargets().put(SkillLevel.ULTRA_GREEN, Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("randomizeOrigin")) { // Legacy, 0.49.7 Removal + retVal.getRandomOriginOptions().setRandomizeOrigin(Boolean.parseBoolean(wn2.getTextContent())); + } else if (wn2.getNodeName().equalsIgnoreCase("randomizeDependentOrigin")) { // Legacy, 0.49.7 Removal + retVal.getRandomOriginOptions().setRandomizeDependentOrigin(Boolean.parseBoolean(wn2.getTextContent())); + } else if (wn2.getNodeName().equalsIgnoreCase("originSearchRadius")) { // Legacy, 0.49.7 Removal + retVal.getRandomOriginOptions().setOriginSearchRadius(Integer.parseInt(wn2.getTextContent())); + } else if (wn2.getNodeName().equalsIgnoreCase("extraRandomOrigin")) { // Legacy, 0.49.7 Removal + 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("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); + retVal.setUseRandomDependentAddition(value); + } else if (wn2.getNodeName().equalsIgnoreCase("dependentsNeverLeave")) { // Legacy - 0.49.7 Removal + retVal.setUseRandomDependentRemoval(!Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("marriageAgeRange")) { // Legacy - 0.49.6 Removal + retVal.setRandomMarriageAgeRange(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("useRandomMarriages")) { // Legacy - 0.49.6 Removal + retVal.setRandomMarriageMethod(Boolean.parseBoolean(wn2.getTextContent().trim()) + ? RandomMarriageMethod.DICE_ROLL : RandomMarriageMethod.NONE); + } else if (wn2.getNodeName().equalsIgnoreCase("logMarriageNameChange")) { // Legacy - 0.49.6 Removal + retVal.setLogMarriageNameChanges(Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("randomMarriageSurnameWeights")) { // Legacy - 0.49.6 Removal + final String[] values = wn2.getTextContent().split(","); + if (values.length == 13) { + final MergingSurnameStyle[] marriageSurnameStyles = MergingSurnameStyle.values(); + for (int i = 0; i < values.length; i++) { + retVal.getMarriageSurnameWeights().put(marriageSurnameStyles[i], Integer.parseInt(values[i])); + } + } else if (values.length == 9) { + retVal.migrateMarriageSurnameWeights47(values); + } else { + logger.error("Unknown length of randomMarriageSurnameWeights"); + } + } else if (wn2.getNodeName().equalsIgnoreCase("logConception")) { // Legacy - 0.49.4 Removal + retVal.setLogProcreation(Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("staticRATs")) { // Legacy - 0.49.4 Removal + retVal.setUseStaticRATs(true); + } else if (wn2.getNodeName().equalsIgnoreCase("ignoreRatEra")) { // Legacy - 0.49.4 Removal + retVal.setIgnoreRATEra(true); + } else if (wn2.getNodeName().equalsIgnoreCase("clanPriceModifier")) { // Legacy - 0.49.3 Removal + final double value = Double.parseDouble(wn2.getTextContent()); + retVal.setClanUnitPriceMultiplier(value); + retVal.setClanPartPriceMultiplier(value); + } else if (wn2.getNodeName().equalsIgnoreCase("usedPartsValueA")) { // Legacy - 0.49.3 Removal + retVal.getUsedPartPriceMultipliers()[0] = Double.parseDouble(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("usedPartsValueB")) { // Legacy - 0.49.3 Removal + retVal.getUsedPartPriceMultipliers()[1] = Double.parseDouble(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("usedPartsValueC")) { // Legacy - 0.49.3 Removal + retVal.getUsedPartPriceMultipliers()[2] = Double.parseDouble(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("usedPartsValueD")) { // Legacy - 0.49.3 Removal + retVal.getUsedPartPriceMultipliers()[3] = Double.parseDouble(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("usedPartsValueE")) { // Legacy - 0.49.3 Removal + retVal.getUsedPartPriceMultipliers()[4] = Double.parseDouble(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("usedPartsValueF")) { // Legacy - 0.49.3 Removal + retVal.getUsedPartPriceMultipliers()[5] = Double.parseDouble(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("damagedPartsValue")) { // Legacy - 0.49.3 Removal + retVal.setDamagedPartsValueMultiplier(Double.parseDouble(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("canceledOrderReimbursement")) { // Legacy - 0.49.3 Removal + retVal.setCancelledOrderRefundMultiplier(Double.parseDouble(wn2.getTextContent().trim())); + + // Removed in 0.47.* + } else if (wn2.getNodeName().equalsIgnoreCase("personnelMarketType")) { // Legacy + retVal.setPersonnelMarketName(PersonnelMarket.getTypeName(Integer.parseInt(wn2.getTextContent().trim()))); + } else if (wn2.getNodeName().equalsIgnoreCase("useAtBCapture")) { // Legacy + if (Boolean.parseBoolean(wn2.getTextContent().trim())) { + retVal.setPrisonerCaptureStyle(PrisonerCaptureStyle.ATB); + retVal.setUseAtBPrisonerDefection(true); + retVal.setUseAtBPrisonerRansom(true); + } + } else if (wn2.getNodeName().equalsIgnoreCase("intensity")) { // Legacy + double intensity = Double.parseDouble(wn2.getTextContent().trim()); + + retVal.atbBattleChance[AtBLanceRole.FIGHTING.ordinal()] = (int) Math.round(((40.0 * intensity) / (40.0 * intensity + 60.0)) * 100.0 + 0.5); + retVal.atbBattleChance[AtBLanceRole.DEFENCE.ordinal()] = (int) Math.round(((20.0 * intensity) / (20.0 * intensity + 80.0)) * 100.0 + 0.5); + retVal.atbBattleChance[AtBLanceRole.SCOUTING.ordinal()] = (int) Math.round(((60.0 * intensity) / (60.0 * intensity + 40.0)) * 100.0 + 0.5); + retVal.atbBattleChance[AtBLanceRole.TRAINING.ordinal()] = (int) Math.round(((10.0 * intensity) / (10.0 * intensity + 90.0)) * 100.0 + 0.5); + } else if (wn2.getNodeName().equalsIgnoreCase("personnelMarketType")) { // Legacy + retVal.personnelMarketName = PersonnelMarket.getTypeName(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("capturePrisoners")) { // Legacy + retVal.setPrisonerCaptureStyle(Boolean.parseBoolean(wn2.getTextContent().trim()) + ? PrisonerCaptureStyle.TAHARQA : PrisonerCaptureStyle.NONE); + } else if (wn2.getNodeName().equalsIgnoreCase("startGameDelay")) { // Legacy + MekHQ.getMHQOptions().setStartGameDelay(Integer.parseInt(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("historicalDailyLog")) { // Legacy + MekHQ.getMHQOptions().setHistoricalDailyLog(Boolean.parseBoolean(wn2.getTextContent().trim())); + } else if (wn2.getNodeName().equalsIgnoreCase("useUnitRating") // Legacy + || wn2.getNodeName().equalsIgnoreCase("useDragoonRating")) { // Legacy + if (!Boolean.parseBoolean(wn2.getTextContent())) { + retVal.setUnitRatingMethod(UnitRatingMethod.NONE); + } + } else if (wn2.getNodeName().equalsIgnoreCase("probPhenoMW")) { // Legacy + retVal.phenotypeProbabilities[Phenotype.MEKWARRIOR.ordinal()] = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("probPhenoBA")) { // Legacy + retVal.phenotypeProbabilities[Phenotype.ELEMENTAL.ordinal()] = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("probPhenoAero")) { // Legacy + retVal.phenotypeProbabilities[Phenotype.AEROSPACE.ordinal()] = Integer.parseInt(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("probPhenoVee")) { // Legacy + retVal.phenotypeProbabilities[Phenotype.VEHICLE.ordinal()] = Integer.parseInt(wn2.getTextContent().trim()); } } catch (Exception ex) { logger.error(ex, "Unknown Exception: generationCampaignOptionsFromXML"); @@ -6179,19 +6311,19 @@ public void migrateMarriageSurnameWeights47(final String... values) { } } - // Now we need to test to figure out the weights have changed. If not, we will - // keep the - // new default values. If they have, we save their changes and add the new - // surname weights - if ((weights[0] != getMarriageSurnameWeights().get(MergingSurnameStyle.NO_CHANGE)) - || (weights[1] != getMarriageSurnameWeights().get(MergingSurnameStyle.YOURS) + 5) - || (weights[2] != getMarriageSurnameWeights().get(MergingSurnameStyle.SPOUSE) + 5) - || (weights[3] != getMarriageSurnameWeights().get(MergingSurnameStyle.HYPHEN_SPOUSE) + 5) - || (weights[4] != getMarriageSurnameWeights().get(MergingSurnameStyle.BOTH_HYPHEN_SPOUSE) + 5) - || (weights[5] != getMarriageSurnameWeights().get(MergingSurnameStyle.HYPHEN_YOURS) + 5) - || (weights[6] != getMarriageSurnameWeights().get(MergingSurnameStyle.BOTH_HYPHEN_YOURS) + 5) - || (weights[7] != getMarriageSurnameWeights().get(MergingSurnameStyle.MALE)) - || (weights[8] != getMarriageSurnameWeights().get(MergingSurnameStyle.FEMALE))) { + // Now we need to test it to figure out the weights have changed. If not, we will keep the + // new default values. If they have, we save their changes and add the new surname weights + if ( + (weights[0] != getMarriageSurnameWeights().get(MergingSurnameStyle.NO_CHANGE)) + || (weights[1] != getMarriageSurnameWeights().get(MergingSurnameStyle.YOURS) + 5) + || (weights[2] != getMarriageSurnameWeights().get(MergingSurnameStyle.SPOUSE) + 5) + || (weights[3] != getMarriageSurnameWeights().get(MergingSurnameStyle.HYPHEN_SPOUSE) + 5) + || (weights[4] != getMarriageSurnameWeights().get(MergingSurnameStyle.BOTH_HYPHEN_SPOUSE) + 5) + || (weights[5] != getMarriageSurnameWeights().get(MergingSurnameStyle.HYPHEN_YOURS) + 5) + || (weights[6] != getMarriageSurnameWeights().get(MergingSurnameStyle.BOTH_HYPHEN_YOURS) + 5) + || (weights[7] != getMarriageSurnameWeights().get(MergingSurnameStyle.MALE)) + || (weights[8] != getMarriageSurnameWeights().get(MergingSurnameStyle.FEMALE)) + ) { getMarriageSurnameWeights().put(MergingSurnameStyle.NO_CHANGE, weights[0]); getMarriageSurnameWeights().put(MergingSurnameStyle.YOURS, weights[1]); getMarriageSurnameWeights().put(MergingSurnameStyle.SPOUSE, weights[2]); diff --git a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java index b0333ec883..8999037e6e 100644 --- a/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java +++ b/MekHQ/src/mekhq/campaign/io/CampaignXmlParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2018-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,44 +18,12 @@ */ package mekhq.campaign.io; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; - -import javax.xml.parsers.DocumentBuilder; - -import org.apache.commons.lang3.StringUtils; -import org.w3c.dom.DOMException; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import megamek.Version; import megamek.client.generator.RandomGenderGenerator; import megamek.client.generator.RandomNameGenerator; import megamek.client.ui.swing.util.PlayerColour; import megamek.common.Entity; -import megamek.common.EntityMovementMode; -import megamek.common.Jumpship; -import megamek.common.Mek; -import megamek.common.MekSummaryCache; -import megamek.common.MiscType; -import megamek.common.Mounted; -import megamek.common.SmallCraft; -import megamek.common.Tank; +import megamek.common.*; import megamek.common.annotations.Nullable; import megamek.common.icons.Camouflage; import megamek.common.weapons.bayweapons.BayWeapon; @@ -63,12 +31,7 @@ import mekhq.MekHQ; import mekhq.NullEntityException; import mekhq.Utilities; -import mekhq.campaign.Campaign; -import mekhq.campaign.CampaignOptions; -import mekhq.campaign.CurrentLocation; -import mekhq.campaign.Kill; -import mekhq.campaign.RandomSkillPreferences; -import mekhq.campaign.Warehouse; +import mekhq.campaign.*; import mekhq.campaign.againstTheBot.AtBConfiguration; import mekhq.campaign.finances.Finances; import mekhq.campaign.force.Force; @@ -81,20 +44,8 @@ import mekhq.campaign.mission.Mission; import mekhq.campaign.mission.Scenario; import mekhq.campaign.mod.am.InjuryTypes; -import mekhq.campaign.parts.EnginePart; -import mekhq.campaign.parts.MekActuator; -import mekhq.campaign.parts.MekLocation; -import mekhq.campaign.parts.MissingEnginePart; -import mekhq.campaign.parts.MissingMekActuator; -import mekhq.campaign.parts.MissingPart; -import mekhq.campaign.parts.Part; -import mekhq.campaign.parts.equipment.AmmoBin; -import mekhq.campaign.parts.equipment.EquipmentPart; -import mekhq.campaign.parts.equipment.HeatSink; -import mekhq.campaign.parts.equipment.MASC; -import mekhq.campaign.parts.equipment.MissingAmmoBin; -import mekhq.campaign.parts.equipment.MissingEquipmentPart; -import mekhq.campaign.parts.equipment.MissingMASC; +import mekhq.campaign.parts.*; +import mekhq.campaign.parts.equipment.*; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.PersonnelOptions; import mekhq.campaign.personnel.SkillType; @@ -115,12 +66,20 @@ import mekhq.io.idReferenceClasses.PersonIdReference; import mekhq.module.atb.AtBEventProcessor; import mekhq.utilities.MHQXMLUtility; +import org.apache.commons.lang3.StringUtils; +import org.w3c.dom.*; + +import javax.xml.parsers.DocumentBuilder; +import java.io.*; +import java.time.LocalDate; +import java.util.*; +import java.util.Map.Entry; public class CampaignXmlParser { - private static final MMLogger logger = MMLogger.create(CampaignXmlParser.class); + private final InputStream is; + private final MekHQ app; - private InputStream is; - private MekHQ app; + private static final MMLogger logger = MMLogger.create(CampaignXmlParser.class); public CampaignXmlParser(InputStream is, MekHQ app) { this.is = is; @@ -588,6 +547,21 @@ public Campaign parse() throws CampaignXmlParseException, NullEntityException { retVal.setUnitRating(null); + // this is used to handle characters from pre-50.01 campaigns + retVal.getPersonnel().stream() + .filter(person -> person.getJoinedCampaign() == null) + .forEach(person -> { + if (person.getRecruitment() != null) { + person.setJoinedCampaign(person.getRecruitment()); + logger.info("{} doesn't have a date recorded showing when they joined the campaign. Using recruitment date.", + person.getFullTitle()); + } else { + person.setJoinedCampaign(retVal.getLocalDate()); + logger.info("{} doesn't have a date recorded showing when they joined the campaign. Using current date.", + person.getFullTitle()); + } + }); + logger.info("Load of campaign file complete!"); return retVal; @@ -616,8 +590,7 @@ private void fixupUnitTechProblems(Campaign retVal) { tech.removeTechUnit(u); } if (null != reason) { - logger - .warn(String.format("Tech %s %s %s (fixed)", tech.getFullName(), reason, unitDesc)); + logger.warn(String.format("Tech %s %s %s (fixed)", tech.getFullName(), reason, unitDesc)); } } } @@ -883,9 +856,7 @@ private static void processPersonnelNodes(Campaign retVal, Node wn, Version vers if (!wn2.getNodeName().equalsIgnoreCase("person")) { // Error condition of sorts! // Errr, what should we do here? - String message = String.format("Unknown node type not loaded in Personnel nodes: {}", - wn2.getNodeName()); - logger.error(message); + logger.error("Unknown node type not loaded in Personnel nodes: {}", wn2.getNodeName()); continue; } @@ -979,8 +950,7 @@ private static void processSpecialAbilityNodes(Campaign retVal, Node wn, Version if (!wn2.getNodeName().equalsIgnoreCase("ability")) { // Error condition of sorts! // Errr, what should we do here? - logger - .error("Unknown node type not loaded in Special Ability nodes: " + wn2.getNodeName()); + logger.error("Unknown node type not loaded in Special Ability nodes: " + wn2.getNodeName()); continue; } SpecialAbility.generateInstanceFromXML(wn2, options, version); @@ -1032,16 +1002,14 @@ private static boolean processCustom(Campaign retVal, Node wn) { File customsDir = new File(sCustomsDir); if (!customsDir.exists()) { if (!customsDir.mkdir()) { - logger - .error("Failed to create directory " + sCustomsDir + ", and therefore cannot save the unit."); + logger.error("Failed to create directory " + sCustomsDir + ", and therefore cannot save the unit."); return false; } } File customsDirCampaign = new File(sCustomsDirCampaign); if (!customsDirCampaign.exists()) { if (!customsDirCampaign.mkdir()) { - logger.error( - "Failed to create directory " + sCustomsDirCampaign + ", and therefore cannot save the unit."); + logger.error("Failed to create directory " + sCustomsDirCampaign + ", and therefore cannot save the unit."); return false; } } @@ -1581,9 +1549,9 @@ private static void updatePlanetaryEventsFromXML(Node wn) { } } - // region Migration Methods - // region Ancestry Migration - private static Map> ancestryMigrationMap = new HashMap<>(); + //region Migration Methods + //region Ancestry Migration + private static final Map> ancestryMigrationMap = new HashMap<>(); /** * This method is used to add people to the ancestry migration map that is used @@ -1651,8 +1619,7 @@ private static void migrateAncestorNodes(Campaign campaign, Node wn) { person.getGenealogy().addFamilyMember(FamilialRelationshipType.PARENT, father); father.getGenealogy().addFamilyMember(FamilialRelationshipType.CHILD, person); } else { - logger.warn( - "Person with id " + father.getId() + "does not exist, skipping adding Genealogy for them."); + logger.warn("Person with id " + father.getId() + "does not exist, skipping adding Genealogy for them."); } if (mother == null) { @@ -1661,8 +1628,7 @@ private static void migrateAncestorNodes(Campaign campaign, Node wn) { person.getGenealogy().addFamilyMember(FamilialRelationshipType.PARENT, mother); mother.getGenealogy().addFamilyMember(FamilialRelationshipType.CHILD, person); } else { - logger.warn("Person with id " + mother.getId() - + " does not exist, skipping adding Genealogy for them."); + logger.warn("Person with id " + mother.getId() + " does not exist, skipping adding Genealogy for them."); } } } diff --git a/MekHQ/src/mekhq/campaign/mission/AtBContract.java b/MekHQ/src/mekhq/campaign/mission/AtBContract.java index 897e2beedf..4dd4b458b0 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBContract.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBContract.java @@ -2,7 +2,7 @@ * AtBContract.java * * Copyright (c) 2014 Carl Spain. All rights reserved. - * Copyright (c) 2020 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2020-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -40,6 +40,7 @@ import megamek.common.MekSummary; import megamek.common.UnitType; import megamek.common.annotations.Nullable; +import megamek.common.enums.Gender; import megamek.common.enums.SkillLevel; import megamek.common.icons.Camouflage; import megamek.common.loaders.EntityLoadingException; @@ -500,12 +501,12 @@ public void doBonusRoll(Campaign c) { int roll = Compute.d6(); switch (roll) { case 1: /* 1d6 dependents */ - if (c.getCampaignOptions().getRandomDependentMethod().isAgainstTheBot() - && c.getCampaignOptions().isUseRandomDependentAddition()) { + if (c.getCampaignOptions().isUseRandomDependentAddition()) { number = Compute.d6(); c.addReport("Bonus: " + number + " dependent" + ((number > 1) ? "s" : "")); + for (int i = 0; i < number; i++) { - Person p = c.newDependent(false); + Person p = c.newDependent(false, Gender.RANDOMIZE); c.recruitPerson(p); } } diff --git a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java index 88347aec21..ab3f3e4cd7 100644 --- a/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java +++ b/MekHQ/src/mekhq/campaign/mission/AtBDynamicScenarioFactory.java @@ -2031,7 +2031,16 @@ private static Entity getEntityByName(String name, String factionCode, SkillLeve RandomNameGenerator rng = RandomNameGenerator.getInstance(); rng.setChosenFaction(faction.getNameGenerator()); - Gender gender = RandomGenderGenerator.generate(); + + Gender gender; + int nonBinaryDiceSize = campaign.getCampaignOptions().getNonBinaryDiceSize(); + + if ((nonBinaryDiceSize > 0) && (Compute.randomInt(nonBinaryDiceSize) == 0)) { + gender = RandomGenderGenerator.generateOther(); + } else { + gender = RandomGenderGenerator.generate(); + } + String[] crewNameArray = rng.generateGivenNameSurnameSplit(gender, faction.isClan(), faction.getShortName()); String crewName = crewNameArray[0]; crewName += !StringUtility.isNullOrBlank(crewNameArray[1]) ? ' ' + crewNameArray[1] : ""; diff --git a/MekHQ/src/mekhq/campaign/mission/BotForceRandomizer.java b/MekHQ/src/mekhq/campaign/mission/BotForceRandomizer.java index b35691b696..8a922275ba 100644 --- a/MekHQ/src/mekhq/campaign/mission/BotForceRandomizer.java +++ b/MekHQ/src/mekhq/campaign/mission/BotForceRandomizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - The Megamek Team. All Rights Reserved. + * Copyright (c) 2021-2024 - The Megamek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,17 +18,6 @@ */ package mekhq.campaign.mission; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; - -import org.apache.commons.math3.distribution.GammaDistribution; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - import megamek.Version; import megamek.client.generator.RandomGenderGenerator; import megamek.client.generator.RandomNameGenerator; @@ -36,13 +25,7 @@ import megamek.client.generator.skillGenerators.AbstractSkillGenerator; import megamek.client.generator.skillGenerators.TaharqaSkillGenerator; import megamek.codeUtilities.StringUtility; -import megamek.common.Compute; -import megamek.common.Crew; -import megamek.common.Entity; -import megamek.common.EntityWeightClass; -import megamek.common.MekFileParser; -import megamek.common.MekSummary; -import megamek.common.UnitType; +import megamek.common.*; import megamek.common.annotations.Nullable; import megamek.common.enums.Gender; import megamek.common.enums.SkillLevel; @@ -57,6 +40,12 @@ import mekhq.campaign.universe.IUnitGenerator; import mekhq.campaign.universe.UnitGeneratorParameters; import mekhq.utilities.MHQXMLUtility; +import org.apache.commons.math3.distribution.GammaDistribution; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.PrintWriter; +import java.util.*; /** * A class that can be used to generate a random force with some parameters. @@ -270,7 +259,7 @@ public void setError(double d) { * given parameters. The * intent is that this function is called from GameThread when the game is * started. - * + * * @param playerUnits A List of Units for the player's deployed force in * the relevant scenario. This * is used to determine the total points allowed for @@ -367,7 +356,7 @@ public List generateForce(List playerUnits, List botFixedE * given parameters. The * intent is that this function is called from GameThread when the game is * started. - * + * * @param playerUnits A List of Units for the player's deployed force in * the relevant scenario. This * is used to determine the total points allowed for @@ -387,7 +376,7 @@ public List generateForce(List playerUnits, List botFixedE * 1 to generate entities individually. The larger this number is the greater * the chance of overshooting * the target number of points. - * + * * @param size an int giving the number of units to generate * @param uType The UnitType of generated units * @param weightClass an int giving the weight class of generated units. The @@ -483,10 +472,19 @@ public Entity getEntity(int uType, int weightClass, Campaign campaign) { RandomNameGenerator rng = RandomNameGenerator.getInstance(); rng.setChosenFaction(faction.getNameGenerator()); - Gender gender = RandomGenderGenerator.generate(); + + Gender gender; + int nonBinaryDiceSize = campaign.getCampaignOptions().getNonBinaryDiceSize(); + + if ((nonBinaryDiceSize > 0) && (Compute.randomInt(nonBinaryDiceSize) == 0)) { + gender = RandomGenderGenerator.generateOther(); + } else { + gender = RandomGenderGenerator.generate(); + } + String[] crewNameArray = rng.generateGivenNameSurnameSplit(gender, faction.isClan(), faction.getShortName()); String crewName = crewNameArray[0]; - crewName += !StringUtility.isNullOrBlank(crewNameArray[1]) ? " " + crewNameArray[1] : ""; + crewName += !StringUtility.isNullOrBlank(crewNameArray[1]) ? ' ' + crewNameArray[1] : ""; Map> extraData = new HashMap<>(); Map innerMap = new HashMap<>(); @@ -537,7 +535,7 @@ public Entity getEntity(int uType, int weightClass, Campaign campaign) { if (!phenotype.isNone()) { String bloodname = Bloodname.randomBloodname(faction.getShortName(), phenotype, campaign.getGameYear()).getName(); - crewName += " " + bloodname; + crewName += ' ' + bloodname; innerMap.put(Crew.MAP_BLOODNAME, bloodname); innerMap.put(Crew.MAP_PHENOTYPE, phenotype.name()); } @@ -556,7 +554,7 @@ public Entity getEntity(int uType, int weightClass, Campaign campaign) { * This function samples from the given gamma distribution to get a random * weight class. Results are trimmed * to reasonable values and rounded to integers. - * + * * @param gamma The GammaDistribution from which a random value is drawn * @return and integer giving the sampled weight class */ @@ -572,7 +570,7 @@ private int sampleWeightClass(GammaDistribution gamma) { * and can refer to different things depending on the selected BalancingMethod. * The maximum points are defined by * a multiple of the player unit points. - * + * * @param playerUnits A List of Units from the player's units assigned to a * given scenario * @return a double giving the targeted maximum points for the generated force. @@ -592,7 +590,7 @@ private double calculateMaxPoints(List playerUnits) { * entities that are part of the BotForce. * The term "points" is abstract and can refer to different things depending on * the selected BalancingMethod. - * + * * @param botEntities - A List of Entities, typically specified as fixed units * in BotForce * @return a double giving the starting points already used by the fixed units @@ -611,7 +609,7 @@ private double calculateStartingPoints(List botEntities) { * This function calculates how many "points" a given entity counts for. The use * of points is abstract and * will be determined differently depending on the provided BalancingMethod - * + * * @param e - an Entity * @return a double giving the points provided by this entity */ @@ -631,7 +629,7 @@ private double calculatePoints(Entity e) { * WEIGHT_ADJ BalancingMethod. * Units get points by weight, but a multiplier is applied to these weights by * unit type. - * + * * @param e an Entity * @return a double indicating the adjusted weight points of a unit. */ @@ -679,7 +677,7 @@ private static double getAdjustedWeightPoints(Entity e) { /** * Calculates the mean weight class of a List of Units - * + * * @param playerUnits - A List of Units * @return a double indicating the mean weight class */ @@ -699,27 +697,23 @@ private double calculateMeanWeightClass(List playerUnits) { } public String getShortDescription() { - StringBuilder sb = new StringBuilder(); - sb.append(forceMultiplier); - sb.append(" ("); - sb.append(balancingMethod.toString()); - sb.append(")"); - return sb.toString(); + String sb = forceMultiplier + " (" + balancingMethod.toString() + ')'; + return sb; } /** * This method returns a description of the random parameters of this object * that will be shown in the * ScenarioViewPanel - * + * * @return a String giving the description. */ public String getDescription(Campaign campaign) { StringBuilder sb = new StringBuilder(); sb.append(Factions.getInstance().getFaction(factionCode).getFullName(campaign.getGameYear())); - sb.append(" "); + sb.append(' '); sb.append(skill.toString()); - sb.append(" "); + sb.append(' '); String typeDesc = UnitType.getTypeDisplayableName(unitType); if (percentConventional > 0) { typeDesc = typeDesc + " and Conventional"; @@ -729,7 +723,7 @@ public String getDescription(Campaign campaign) { sb.append(forceMultiplier); sb.append(" multiplier ("); sb.append(balancingMethod.toString()); - sb.append(")"); + sb.append(')'); return sb.toString(); } diff --git a/MekHQ/src/mekhq/campaign/personnel/Person.java b/MekHQ/src/mekhq/campaign/personnel/Person.java index 7046bdbde8..7c6a39c1c6 100644 --- a/MekHQ/src/mekhq/campaign/personnel/Person.java +++ b/MekHQ/src/mekhq/campaign/personnel/Person.java @@ -137,6 +137,7 @@ public class Person { private String biography; private LocalDate birthday; + private LocalDate joinedCampaign; private LocalDate recruitment; private LocalDate lastRankChangeDate; private LocalDate dateOfDeath; @@ -372,6 +373,7 @@ public Person(final String preNominal, final String givenName, final String surn resetMinutesLeft(); // this assigns minutesLeft and overtimeLeft dateOfDeath = null; recruitment = null; + joinedCampaign = null; lastRankChangeDate = null; autoAwardSupportPoints = 0; retirement = null; @@ -905,7 +907,7 @@ public String getSecondaryRoleDesc() { return getSecondaryRole().getName(isClanPersonnel()); } - public boolean canPerformRole(final PersonnelRole role, final boolean primary) { + public boolean canPerformRole(LocalDate today, Person person, final PersonnelRole role, final boolean primary) { if (primary) { // Primary Role: // We only do a few here, as it is better on the UX-side to correct the issues @@ -941,6 +943,10 @@ public boolean canPerformRole(final PersonnelRole role, final boolean primary) { } } + if (person.isChild(today)) { + return false; + } + return switch (role) { case MEKWARRIOR -> hasSkill(SkillType.S_GUN_MEK) && hasSkill(SkillType.S_PILOT_MEK); case LAM_PILOT -> @@ -1015,7 +1021,7 @@ public void changeStatus(final Campaign campaign, final LocalDate today, campaign.addReport(String.format(resources.getString("recoveredPoW.report"), getHyperlinkedFullTitle())); ServiceLogger.recoveredPoW(this, campaign.getLocalDate()); - } else if (getStatus().isOnLeave()) { + } else if (getStatus().isOnLeave() || getStatus().isOnMaternityLeave()) { campaign.addReport(String.format(resources.getString("returnedFromLeave.report"), getHyperlinkedFullTitle())); ServiceLogger.returnedFromLeave(this, campaign.getLocalDate()); @@ -1374,6 +1380,14 @@ public int getAge(LocalDate today) { return Math.toIntExact(ChronoUnit.YEARS.between(getBirthday(), today)); } + public @Nullable LocalDate getJoinedCampaign() { + return joinedCampaign; + } + + public void setJoinedCampaign(final @Nullable LocalDate joinedCampaign) { + this.joinedCampaign = joinedCampaign; + } + public @Nullable LocalDate getRecruitment() { return recruitment; } @@ -2045,6 +2059,8 @@ public void writeToXML(final PrintWriter pw, int indent, final Campaign campaign MHQXMLUtility.writeSimpleXMLTag(pw, indent, "birthday", getBirthday()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "deathday", getDateOfDeath()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "recruitment", getRecruitment()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "joinedCampaign", getJoinedCampaign()); + MHQXMLUtility.writeSimpleXMLTag(pw, indent, "lastRankChangeDate", getLastRankChangeDate()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "autoAwardSupportPoints", getAutoAwardSupportPoints()); MHQXMLUtility.writeSimpleXMLTag(pw, indent, "retirement", getRetirement()); @@ -2373,6 +2389,8 @@ public static Person generateInstanceFromXML(Node wn, Campaign c, Version versio retVal.dateOfDeath = MHQXMLUtility.parseDate(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("recruitment")) { retVal.recruitment = MHQXMLUtility.parseDate(wn2.getTextContent().trim()); + } else if (wn2.getNodeName().equalsIgnoreCase("joinedCampaign")) { + retVal.joinedCampaign = MHQXMLUtility.parseDate(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("lastRankChangeDate")) { retVal.lastRankChangeDate = MHQXMLUtility.parseDate(wn2.getTextContent().trim()); } else if (wn2.getNodeName().equalsIgnoreCase("autoAwardSupportPoints")) { diff --git a/MekHQ/src/mekhq/campaign/personnel/death/AbstractDeath.java b/MekHQ/src/mekhq/campaign/personnel/death/AbstractDeath.java index 53988dce0b..29ee8487de 100644 --- a/MekHQ/src/mekhq/campaign/personnel/death/AbstractDeath.java +++ b/MekHQ/src/mekhq/campaign/personnel/death/AbstractDeath.java @@ -116,7 +116,7 @@ public Map>> get /** * This is used to determine if a person can die. - * + * * @param person the person to determine for * @param ageGroup the age group of the person in question * @param randomDeath if this is for random death or manual death @@ -144,7 +144,7 @@ public Map>> get // region New Day /** * Processes new day random death for an individual. - * + * * @param campaign the campaign to process * @param today the current day * @param person the person to process diff --git a/MekHQ/src/mekhq/campaign/personnel/divorce/AbstractDivorce.java b/MekHQ/src/mekhq/campaign/personnel/divorce/AbstractDivorce.java index 015c16a91b..1d4642f854 100644 --- a/MekHQ/src/mekhq/campaign/personnel/divorce/AbstractDivorce.java +++ b/MekHQ/src/mekhq/campaign/personnel/divorce/AbstractDivorce.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,6 +18,7 @@ */ package mekhq.campaign.personnel.divorce; +import megamek.common.Compute; import megamek.common.annotations.Nullable; import mekhq.MekHQ; import mekhq.campaign.Campaign; @@ -26,11 +27,14 @@ import mekhq.campaign.log.PersonalLogger; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.enums.FormerSpouseReason; +import mekhq.campaign.personnel.enums.PersonnelStatus; import mekhq.campaign.personnel.enums.RandomDivorceMethod; import mekhq.campaign.personnel.enums.SplittingSurnameStyle; import mekhq.campaign.personnel.familyTree.FormerSpouse; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; import java.util.ResourceBundle; /** @@ -150,8 +154,12 @@ public void setUseRandomPrisonerDivorce(final boolean useRandomPrisonerDivorce) return resources.getString("cannotDivorce.RandomPrisoner.text"); } else if (!isUseRandomPrisonerDivorce() && person.getGenealogy().getSpouse().getPrisonerStatus().isCurrentPrisoner()) { return resources.getString("cannotDivorce.RandomPrisonerSpouse.text"); + } else if (!person.equals(person.getGenealogy().getOriginSpouse())) { + return resources.getString("cannotDivorce.RandomNotOriginSpouse.text"); } + final boolean sameSex = person.getGenealogy().getSpouse().getGender() == person.getGender(); + if (!isUseRandomOppositeSexDivorce() && !sameSex) { return resources.getString("cannotDivorce.OppositeSexDivorceDisabled.text"); } else if (!isUseRandomSameSexDivorce() && sameSex) { @@ -165,7 +173,7 @@ public void setUseRandomPrisonerDivorce(final boolean useRandomPrisonerDivorce) /** * This is a standardization method for the divorce surname style to use when a person's spouse * dies. - * + *

* TODO : I should be part of AbstractDeath * * @param campaign the campaign the person is in @@ -234,46 +242,131 @@ public void divorce(final Campaign campaign, final LocalDate today, final Person spouse.getGenealogy().setSpouse(null); } - // Add to former spouse list + // Add to the former spouse list spouse.getGenealogy().addFormerSpouse(new FormerSpouse(origin, today, reason)); origin.getGenealogy().addFormerSpouse(new FormerSpouse(spouse, today, reason)); + // Clear origin spouses + origin.getGenealogy().setOriginSpouse(null); + spouse.getGenealogy().setOriginSpouse(null); + + // roll for removal of marriageable flag + if (Compute.d6(1) <= 2) { + origin.setMarriageable(false); + } + + if (Compute.d6(1) <= 2) { + spouse.setMarriageable(false); + } + + List departingPartners = new ArrayList<>(); + + if (origin.getPrimaryRole().isDependent()) { + departingPartners.add(origin); + } + + if (spouse.getPrimaryRole().isDependent()) { + departingPartners.add(origin); + } + + if (!departingPartners.isEmpty()) { + for (Person departingPartner : departingPartners) { + departingPartner.changeStatus(campaign, today, PersonnelStatus.LEFT); + + for (Person child : departingPartner.getGenealogy().getChildren()) { + int remainingParents = child.getGenealogy().getParents().size(); + + if ((remainingParents == 0) || (Compute.randomInt(2) == 0)) { + child.changeStatus(campaign, today, PersonnelStatus.LEFT); + } + } + } + } + + // Process any relevant loyalty changes + if (campaign.getCampaignOptions().isUseLoyaltyModifiers()) { + if (origin.getStatus().isLeft() && !spouse.getStatus().isLeft()) { + spouse.performRandomizedLoyaltyChange(campaign, false, true); + } else if (!origin.getStatus().isLeft() && spouse.getStatus().isLeft()) { + origin.performRandomizedLoyaltyChange(campaign, false, true); + } else if (origin.getStatus().isLeft() && spouse.getStatus().isLeft()) { + origin.performForcedDirectionLoyaltyChange(campaign, false, false, true); + spouse.performForcedDirectionLoyaltyChange(campaign, false, false, true); + } + } + + // trigger person changed events MekHQ.triggerEvent(new PersonChangedEvent(spouse)); MekHQ.triggerEvent(new PersonChangedEvent(origin)); } + /** + * Processes divorce events that occur as part of a character's background. + * + * @param campaign the campaign associated with the divorce + * @param today the current date of the divorce + * @param origin the person whose background is being divorced + * @param style the splitting surname style to be applied + */ + public void backgroundDivorce(final Campaign campaign, final LocalDate today, final Person origin, + final SplittingSurnameStyle style) { + final Person spouse = origin.getGenealogy().getSpouse(); + + style.apply(campaign, origin, spouse); + + final FormerSpouseReason reason = FormerSpouseReason.DIVORCE; + + PersonalLogger.divorcedFrom(origin, spouse, today); + PersonalLogger.divorcedFrom(spouse, origin, today); + + spouse.setMaidenName(null); + origin.setMaidenName(null); + + spouse.getGenealogy().setSpouse(null); + origin.getGenealogy().setSpouse(null); + + // Add to the former spouse list + spouse.getGenealogy().addFormerSpouse(new FormerSpouse(origin, today, reason)); + origin.getGenealogy().addFormerSpouse(new FormerSpouse(spouse, today, reason)); + + // Clear origin spouses + origin.getGenealogy().setOriginSpouse(null); + spouse.getGenealogy().setOriginSpouse(null); + + // roll for removal of marriageable flag + if (Compute.d6(1) <= 2) { + origin.setMarriageable(false); + } + } + //region New Day /** * Processes new day random divorce for an individual. * @param campaign the campaign to process * @param today the current day * @param person the person to process + * @param isBackground whether the divorce occurred during a character's backstory */ - public void processNewDay(final Campaign campaign, final LocalDate today, final Person person) { + public void processNewWeek(final Campaign campaign, final LocalDate today, final Person person, boolean isBackground) { if (canDivorce(person, true) != null) { return; } - if ((person.getGenealogy().getSpouse().getGender() == person.getGender()) - ? randomSameSexDivorce(person) : randomOppositeSexDivorce(person)) { - divorce(campaign, today, person, SplittingSurnameStyle.WEIGHTED); + if (randomDivorce()) { + if (isBackground) { + backgroundDivorce(campaign, today, person, SplittingSurnameStyle.WEIGHTED); + } else { + divorce(campaign, today, person, SplittingSurnameStyle.WEIGHTED); + } } } //region Random Divorce /** - * This determines if a person will randomly divorce their opposite sex spouse - * @param person the person to determine if they are to randomly divorce their opposite sex spouse - * @return true if the person is to randomly divorce - */ - protected abstract boolean randomOppositeSexDivorce(final Person person); - - /** - * This determines if a person will randomly divorce their same-sex spouse. - * @param person the person who may be randomly divorcing their same-sex spouse + * This determines if a person will randomly divorce their spouse * @return true if the person is to randomly divorce */ - protected abstract boolean randomSameSexDivorce(final Person person); + protected abstract boolean randomDivorce(); //endregion Random Divorce //endregion New Day } diff --git a/MekHQ/src/mekhq/campaign/personnel/divorce/DisabledRandomDivorce.java b/MekHQ/src/mekhq/campaign/personnel/divorce/DisabledRandomDivorce.java index 1cdfeb0fc8..f24ea6d52f 100644 --- a/MekHQ/src/mekhq/campaign/personnel/divorce/DisabledRandomDivorce.java +++ b/MekHQ/src/mekhq/campaign/personnel/divorce/DisabledRandomDivorce.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -19,7 +19,6 @@ package mekhq.campaign.personnel.divorce; import mekhq.campaign.CampaignOptions; -import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.enums.RandomDivorceMethod; public class DisabledRandomDivorce extends AbstractDivorce { @@ -30,12 +29,7 @@ public DisabledRandomDivorce(final CampaignOptions options) { //endregion Constructors @Override - protected boolean randomOppositeSexDivorce(final Person person) { - return false; - } - - @Override - protected boolean randomSameSexDivorce(final Person person) { + protected boolean randomDivorce() { return false; } } diff --git a/MekHQ/src/mekhq/campaign/personnel/divorce/PercentageRandomDivorce.java b/MekHQ/src/mekhq/campaign/personnel/divorce/PercentageRandomDivorce.java deleted file mode 100644 index b4fd1e4591..0000000000 --- a/MekHQ/src/mekhq/campaign/personnel/divorce/PercentageRandomDivorce.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2021 - 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.divorce; - -import megamek.common.Compute; -import mekhq.campaign.CampaignOptions; -import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.enums.RandomDivorceMethod; - -public class PercentageRandomDivorce extends AbstractDivorce { - //region Variable Declarations - private double oppositeSexPercentage; - private double sameSexPercentage; - //endregion Variable Declarations - - //region Constructors - public PercentageRandomDivorce(final CampaignOptions options) { - super(RandomDivorceMethod.PERCENTAGE, options); - setOppositeSexPercentage(options.getPercentageRandomDivorceOppositeSexChance()); - setSameSexPercentage(options.getPercentageRandomDivorceSameSexChance()); - } - //endregion Constructors - - //region Getters/Setters - public double getOppositeSexPercentage() { - return oppositeSexPercentage; - } - - public void setOppositeSexPercentage(final double oppositeSexPercentage) { - this.oppositeSexPercentage = oppositeSexPercentage; - } - - public double getSameSexPercentage() { - return sameSexPercentage; - } - - public void setSameSexPercentage(final double sameSexPercentage) { - this.sameSexPercentage = sameSexPercentage; - } - //endregion Getters/Setters - - @Override - protected boolean randomOppositeSexDivorce(final Person person) { - return Compute.randomFloat() < getOppositeSexPercentage(); - } - - @Override - protected boolean randomSameSexDivorce(final Person person) { - return Compute.randomFloat() < getSameSexPercentage(); - } -} diff --git a/MekHQ/src/mekhq/campaign/personnel/divorce/RandomDivorce.java b/MekHQ/src/mekhq/campaign/personnel/divorce/RandomDivorce.java new file mode 100644 index 0000000000..5226acc6c6 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/personnel/divorce/RandomDivorce.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2021-2024 - 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.divorce; + +import megamek.common.Compute; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.personnel.enums.RandomDivorceMethod; + +/** + * The {@link RandomDivorce} class is an implementation of the {@link AbstractDivorce} class that + * represents a divorce method based on random chance. + * The divorce outcome is determined by rolling a die with a specified number of sides. + */ +public class RandomDivorce extends AbstractDivorce { + //region Variable Declarations + private int divorceDiceSize; + //endregion Variable Declarations + + //region Constructors + /** + * The {@link RandomDivorce} class is an implementation of the {@link AbstractDivorce} class that + * represents a divorce method based on random chance. + */ + public RandomDivorce(final CampaignOptions options) { + super(RandomDivorceMethod.DICE_ROLL, options); + setDivorceDiceSize(options.getRandomDivorceDiceSize()); + } + //endregion Constructors + + /** + * Retrieves the size of the divorce dice. + * + * @return The size of the divorce dice as an integer. + */ + //region Getters/Setters + public int getDivorceDiceSize() { + return divorceDiceSize; + } + + /** + * Sets the size of the divorce dice. + * + * @param divorceDiceSize the size of the dice used to determine divorce outcomes + */ + public void setDivorceDiceSize(final int divorceDiceSize) { + this.divorceDiceSize = divorceDiceSize; + } + //endregion Getters/Setters + + @Override + protected boolean randomDivorce() { + if (divorceDiceSize == 0) { + return false; + } else if (divorceDiceSize == 1) { + return true; + } + + return Compute.randomInt(divorceDiceSize) == 0; + } +} diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelStatus.java b/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelStatus.java index bdddcc2f84..5d7794974b 100644 --- a/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelStatus.java +++ b/MekHQ/src/mekhq/campaign/personnel/enums/PersonnelStatus.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2020-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,69 +18,69 @@ */ package mekhq.campaign.personnel.enums; +import megamek.logging.MMLogger; +import mekhq.MekHQ; + import java.util.List; import java.util.ResourceBundle; import java.util.stream.Collectors; import java.util.stream.Stream; -import megamek.logging.MMLogger; -import mekhq.MekHQ; - /** - * TODO : Add PoW, On Leave and AWOL implementations + * Enum class representing personnel status. */ public enum PersonnelStatus { // region Enum Declarations - ACTIVE("PersonnelStatus.ACTIVE.text", "PersonnelStatus.ACTIVE.toolTipText", "PersonnelStatus.ACTIVE.reportText", - "PersonnelStatus.ACTIVE.logText"), - MIA("PersonnelStatus.MIA.text", "PersonnelStatus.MIA.toolTipText", "PersonnelStatus.MIA.reportText", - "PersonnelStatus.MIA.logText"), - POW("PersonnelStatus.POW.text", "PersonnelStatus.POW.toolTipText", "PersonnelStatus.POW.reportText", - "PersonnelStatus.POW.logText"), + ACTIVE("PersonnelStatus.ACTIVE.text", "PersonnelStatus.ACTIVE.toolTipText", + "PersonnelStatus.ACTIVE.reportText", "PersonnelStatus.ACTIVE.logText"), + MIA("PersonnelStatus.MIA.text", "PersonnelStatus.MIA.toolTipText", + "PersonnelStatus.MIA.reportText", "PersonnelStatus.MIA.logText"), + POW("PersonnelStatus.POW.text", "PersonnelStatus.POW.toolTipText", + "PersonnelStatus.POW.reportText", "PersonnelStatus.POW.logText"), 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"), + ON_MATERNITY_LEAVE("PersonnelStatus.ON_MATERNITY_LEAVE.text", "PersonnelStatus.ON_MATERNITY_LEAVE.toolTipText", + "PersonnelStatus.ON_MATERNITY_LEAVE.reportText", "PersonnelStatus.ON_MATERNITY_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"), - SACKED("PersonnelStatus.SACKED.text", "PersonnelStatus.SACKED.toolTipText", "PersonnelStatus.SACKED.reportText", - "PersonnelStatus.SACKED.logText"), - LEFT("PersonnelStatus.LEFT.text", "PersonnelStatus.LEFT.toolTipText", "PersonnelStatus.LEFT.reportText", - "PersonnelStatus.LEFT.logText"), + SACKED("PersonnelStatus.SACKED.text", "PersonnelStatus.SACKED.toolTipText", + "PersonnelStatus.SACKED.reportText", "PersonnelStatus.SACKED.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"), + 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"), HOMICIDE("PersonnelStatus.HOMICIDE.text", "PersonnelStatus.HOMICIDE.toolTipText", "PersonnelStatus.HOMICIDE.reportText", "PersonnelStatus.HOMICIDE.logText"), - WOUNDS("PersonnelStatus.WOUNDS.text", "PersonnelStatus.WOUNDS.toolTipText", "PersonnelStatus.WOUNDS.reportText", - "PersonnelStatus.WOUNDS.logText"), - DISEASE("PersonnelStatus.DISEASE.text", "PersonnelStatus.DISEASE.toolTipText", "PersonnelStatus.DISEASE.reportText", - "PersonnelStatus.DISEASE.logText"), + WOUNDS("PersonnelStatus.WOUNDS.text", "PersonnelStatus.WOUNDS.toolTipText", + "PersonnelStatus.WOUNDS.reportText", "PersonnelStatus.WOUNDS.logText"), + DISEASE("PersonnelStatus.DISEASE.text", "PersonnelStatus.DISEASE.toolTipText", + "PersonnelStatus.DISEASE.reportText", "PersonnelStatus.DISEASE.logText"), ACCIDENTAL("PersonnelStatus.ACCIDENTAL.text", "PersonnelStatus.ACCIDENTAL.toolTipText", "PersonnelStatus.ACCIDENTAL.reportText", "PersonnelStatus.ACCIDENTAL.logText"), NATURAL_CAUSES("PersonnelStatus.NATURAL_CAUSES.text", "PersonnelStatus.NATURAL_CAUSES.toolTipText", "PersonnelStatus.NATURAL_CAUSES.reportText", "PersonnelStatus.NATURAL_CAUSES.logText"), - OLD_AGE("PersonnelStatus.OLD_AGE.text", "PersonnelStatus.OLD_AGE.toolTipText", "PersonnelStatus.OLD_AGE.reportText", - "PersonnelStatus.OLD_AGE.logText"), - MEDICAL_COMPLICATIONS("PersonnelStatus.MEDICAL_COMPLICATIONS.text", - "PersonnelStatus.MEDICAL_COMPLICATIONS.toolTipText", "PersonnelStatus.MEDICAL_COMPLICATIONS.reportText", - "PersonnelStatus.MEDICAL_COMPLICATIONS.logText"), - PREGNANCY_COMPLICATIONS("PersonnelStatus.PREGNANCY_COMPLICATIONS.text", - "PersonnelStatus.PREGNANCY_COMPLICATIONS.toolTipText", "PersonnelStatus.PREGNANCY_COMPLICATIONS.reportText", - "PersonnelStatus.PREGNANCY_COMPLICATIONS.logText"), + OLD_AGE("PersonnelStatus.OLD_AGE.text", "PersonnelStatus.OLD_AGE.toolTipText", + "PersonnelStatus.OLD_AGE.reportText", "PersonnelStatus.OLD_AGE.logText"), + MEDICAL_COMPLICATIONS("PersonnelStatus.MEDICAL_COMPLICATIONS.text", "PersonnelStatus.MEDICAL_COMPLICATIONS.toolTipText", + "PersonnelStatus.MEDICAL_COMPLICATIONS.reportText", "PersonnelStatus.MEDICAL_COMPLICATIONS.logText"), + PREGNANCY_COMPLICATIONS("PersonnelStatus.PREGNANCY_COMPLICATIONS.text", "PersonnelStatus.PREGNANCY_COMPLICATIONS.toolTipText", + "PersonnelStatus.PREGNANCY_COMPLICATIONS.reportText", "PersonnelStatus.PREGNANCY_COMPLICATIONS.logText"), UNDETERMINED("PersonnelStatus.UNDETERMINED.text", "PersonnelStatus.UNDETERMINED.toolTipText", "PersonnelStatus.UNDETERMINED.reportText", "PersonnelStatus.UNDETERMINED.logText"), - SUICIDE("PersonnelStatus.SUICIDE.text", "PersonnelStatus.SUICIDE.toolTipText", "PersonnelStatus.SUICIDE.reportText", - "PersonnelStatus.SUICIDE.logText"); + SUICIDE("PersonnelStatus.SUICIDE.text", "PersonnelStatus.SUICIDE.toolTipText", + "PersonnelStatus.SUICIDE.reportText", "PersonnelStatus.SUICIDE.logText"); // endregion Enum Declarations // region Variable Declarations @@ -91,6 +91,14 @@ public enum PersonnelStatus { // endregion Variable Declarations // region Constructors + /** + * Initializes a new instance of the {@link PersonnelStatus} class with the given parameters. + * + * @param name the name of the personnel status + * @param toolTipText the tooltip text for the personnel status + * @param reportText the report text for the personnel status + * @param logText the log text for the personnel status + */ PersonnelStatus(final String name, final String toolTipText, final String reportText, final String logText) { final ResourceBundle resources = ResourceBundle.getBundle("mekhq.resources.Personnel", @@ -102,127 +110,296 @@ public enum PersonnelStatus { } // endregion Constructors + /** + * Retrieves the tooltip text for a given component. + * + * @return The tooltip text of the component, or null if no tooltip text is set. + */ // region Getters public String getToolTipText() { return toolTipText; } + /** + * Retrieves the report text. + * + * @return The report text as a string. + */ public String getReportText() { return reportText; } + /** + * Retrieves the log text. + * + * @return The log text. + */ public String getLogText() { return logText; } // endregion Getters // region Boolean Comparison Methods + /** + * Checks if the character has the {@code ACTIVE} personnel status. + * + * @return {@code true} if the character has the {@code ACTIVE} personnel status {@code false} + * otherwise. + */ public boolean isActive() { return this == ACTIVE; } + /** + * Checks if the character has the {@code MIA} personnel status. + * + * @return {@code true} if the character has the {@code MIA} personnel status {@code false} + * otherwise. + */ public boolean isMIA() { return this == MIA; } + /** + * Checks if the character has the {@code POW} personnel status. + * + * @return {@code true} if the character has the {@code POW} personnel status {@code false} + * otherwise. + */ public boolean isPoW() { return this == POW; } + /** + * Checks if the character has the {@code ON_LEAVE} personnel status. + * + * @return {@code true} if the character has the {@code ON_LEAVE} personnel status {@code false} + * otherwise. + */ public boolean isOnLeave() { return this == ON_LEAVE; } + /** + * Checks if the character has the {@code ON_MATERNITY_LEAVE} personnel status. + * + * @return {@code true} if the character has the {@code ON_MATERNITY_LEAVE} personnel status + * {@code false} otherwise. + */ + public boolean isOnMaternityLeave() { + return this == ON_MATERNITY_LEAVE; + } + + /** + * Checks if the character has the {@code AWOL} personnel status. + * + * @return {@code true} if the character has the {@code AWOL} personnel status {@code false} + * otherwise. + */ public boolean isAwol() { return this == AWOL; } + /** + * Checks if the character has the {@code RETIRED} personnel status. + * + * @return {@code true} if the character has the {@code RETIRED} personnel status {@code false} + * otherwise. + */ public boolean isRetired() { return this == RETIRED; } + /** + * Checks if the character has the {@code RESIGNED} personnel status. + * + * @return {@code true} if the character has the {@code RESIGNED} personnel status {@code false} + * otherwise. + */ public boolean isResigned() { return this == RESIGNED; } + /** + * Checks if the character has the {@code SACKED} personnel status. + * + * @return {@code true} if the character has the {@code SACKED} personnel status {@code false} + * otherwise. + */ public boolean isSacked() { return this == SACKED; } + /** + * Checks if the character has the {@code LEFT} personnel status. + * + * @return {@code true} if the character has the {@code LEFT} personnel status {@code false} + * otherwise. + */ public boolean isLeft() { return this == LEFT; } + /** + * Checks if the character has the {@code DESERTED} personnel status. + * + * @return {@code true} if the character has the {@code DESERTED} personnel status {@code false} + * otherwise. + */ public boolean isDeserted() { return this == DESERTED; } + /** + * Checks if the character has the {@code DEFECTED} personnel status. + * + * @return {@code true} if the character has the {@code DEFECTED} personnel status {@code false} + * otherwise. + */ public boolean isDefected() { return this == DEFECTED; } + /** + * Checks if the character has the {@code STUDENT} personnel status. + * + * @return {@code true} if the character has the {@code STUDENT} personnel status {@code false} + * otherwise. + */ public boolean isStudent() { return this == STUDENT; } + /** + * Checks if the character has the {@code MISSING} personnel status. + * + * @return {@code true} if the character has the {@code MISSING} personnel status {@code false} + * otherwise. + */ public boolean isMissing() { return this == MISSING; } + /** + * Checks if the character has the {@code KIA} personnel status. + * + * @return {@code true} if the character has the {@code KIA} personnel status {@code false} + * otherwise. + */ public boolean isKIA() { return this == KIA; } + /** + * Checks if the character has the {@code HOMICIDE} personnel status. + * + * @return {@code true} if the character has the {@code HOMICIDE} personnel status {@code false} + * otherwise. + */ public boolean isHomicide() { return this == HOMICIDE; } + /** + * Checks if the character has the {@code WOUNDS} personnel status. + * + * @return {@code true} if the character has the {@code WOUNDS} personnel status {@code false} + * otherwise. + */ public boolean isWounds() { return this == WOUNDS; } + /** + * Checks if the character has the {@code DISEASE} personnel status. + * + * @return {@code true} if the character has the {@code DISEASE} personnel status {@code false} + * otherwise. + */ public boolean isDisease() { return this == DISEASE; } + /** + * Checks if the character has the {@code ACCIDENTAL} personnel status. + * + * @return {@code true} if the character has the {@code ACCIDENTAL} personnel status {@code false} + * otherwise. + */ public boolean isAccidental() { return this == ACCIDENTAL; } + /** + * Checks if the character has the {@code NATURAL_CAUSES} personnel status. + * + * @return {@code true} if the character has the {@code NATURAL_CAUSES} personnel status {@code false} + * otherwise. + */ public boolean isNaturalCauses() { return this == NATURAL_CAUSES; } + /** + * Checks if the character has the {@code OLD_AGE} personnel status. + * + * @return {@code true} if the character has the {@code OLD_AGE} personnel status {@code false} + * otherwise. + */ public boolean isOldAge() { return this == OLD_AGE; } + /** + * Checks if the character has the {@code MEDICAL_COMPLICATIONS} personnel status. + * + * @return {@code true} if the character has the {@code MEDICAL_COMPLICATIONS} personnel status + * {@code false} otherwise. + */ public boolean isMedicalComplications() { return this == MEDICAL_COMPLICATIONS; } + /** + * Checks if the character has the {@code PREGNANCY_COMPLICATIONS} personnel status. + * + * @return {@code true} if the character has the {@code PREGNANCY_COMPLICATIONS} personnel status + * {@code false} otherwise. + */ public boolean isPregnancyComplications() { return this == PREGNANCY_COMPLICATIONS; } + /** + * Checks if the character has the {@code UNDETERMINED} personnel status. + * + * @return {@code true} if the character has the {@code UNDETERMINED} personnel status {@code false} + * otherwise. + */ public boolean isUndetermined() { return this == UNDETERMINED; } + /** + * Checks if the character has the {@code SUICIDE} personnel status. + * + * @return {@code true} if the character has the {@code SUICIDE} personnel status {@code false} + * otherwise. + */ public boolean isSuicide() { return this == SUICIDE; } /** - * @return true if a person is currently absent from the core force, otherwise - * false + * @return {@code true} if a person is currently absent from the core force, otherwise + * {@code false} */ public boolean isAbsent() { - return isMIA() || isPoW() || isOnLeave() || isAwol() || isStudent() || isMissing(); + return isMIA() || isPoW() || isOnLeave() || isOnMaternityLeave() || isAwol() || isStudent() || isMissing(); } /** - * @return true if a person has left the unit, otherwise false + * @return {@code true} if a person has left the unit, otherwise {@code false} */ public boolean isDepartedUnit() { return isDead() || isRetired() || isResigned() || isSacked() || isDeserted() || isDefected() || isMissing() @@ -230,7 +407,7 @@ public boolean isDepartedUnit() { } /** - * @return true if a person is dead, otherwise false + * @return {@code true} if a person is dead, otherwise {@code false} */ public boolean isDead() { return isKIA() || isHomicide() || isWounds() || isDisease() || isAccidental() @@ -239,13 +416,16 @@ public boolean isDead() { } /** - * @return true if a person is dead or MIA, otherwise false + * @return {@code true} if a person is dead or MIA, otherwise {@code false} */ public boolean isDeadOrMIA() { return isDead() || isMIA(); } // endregion Boolean Comparison Methods + /** + * @return The list of implemented personnel statuses. + */ public static List getImplementedStatuses() { return Stream.of(values()) .collect(Collectors.toList()); @@ -253,15 +433,16 @@ public static List getImplementedStatuses() { // region File I/O /** - * @param text containing the PersonnelStatus - * @return the saved PersonnelStatus + * Parses a string representation of {@link PersonnelStatus} into a {@link PersonnelStatus} object. + * If the string representation cannot be parsed, it returns the default {@code PersonnelStatus.ACTIVE}. + * + * @param text The {@link String} representation of {@link PersonnelStatus} + * @return The parsed {@link PersonnelStatus} object or {@code PersonnelStatus.ACTIVE} if parsing fails */ public static PersonnelStatus parseFromString(final String text) { try { return valueOf(text); - } catch (Exception ignored) { - - } + } catch (Exception ignored) {} try { switch (Integer.parseInt(text)) { @@ -311,14 +492,16 @@ public static PersonnelStatus parseFromString(final String text) { return SUICIDE; case 22: return SACKED; + case 23: + return ON_MATERNITY_LEAVE; default: break; } - } catch (Exception ignored) { + } catch (Exception ignored) {} - } String message = String.format("Unable to parse %s into a PersonnelStatus. Returning ACTIVE.", text); + MMLogger.create(PersonnelStatus.class).error(message); return ACTIVE; } diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/RandomDivorceMethod.java b/MekHQ/src/mekhq/campaign/personnel/enums/RandomDivorceMethod.java index c46cda4762..db526631d2 100644 --- a/MekHQ/src/mekhq/campaign/personnel/enums/RandomDivorceMethod.java +++ b/MekHQ/src/mekhq/campaign/personnel/enums/RandomDivorceMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -22,14 +22,25 @@ import mekhq.campaign.CampaignOptions; import mekhq.campaign.personnel.divorce.AbstractDivorce; import mekhq.campaign.personnel.divorce.DisabledRandomDivorce; -import mekhq.campaign.personnel.divorce.PercentageRandomDivorce; +import mekhq.campaign.personnel.divorce.RandomDivorce; import java.util.ResourceBundle; +import static megamek.client.ui.WrapLayout.wordWrap; + +/** + * An enumeration representing the available random divorce methods. + *

+ * The {@link RandomDivorceMethod} enum is used to specify the method of randomly generating a divorce. + * It supports two methods: {@code NONE} and {@code DICE_ROLL}. + *

+ * The {@code NONE} method represents no random divorce generation, and the {@code DICE_ROLL} method + * represents randomly generated divorce using dice rolls. + */ public enum RandomDivorceMethod { //region Enum Declarations NONE("RandomDivorceMethod.NONE.text", "RandomDivorceMethod.NONE.toolTipText"), - PERCENTAGE("RandomDivorceMethod.PERCENTAGE.text", "RandomDivorceMethod.PERCENTAGE.toolTipText"); + DICE_ROLL("RandomDivorceMethod.DICE_ROLL.text", "RandomDivorceMethod.DICE_ROLL.toolTipText"); //endregion Enum Declarations //region Variable Declarations @@ -38,37 +49,61 @@ public enum RandomDivorceMethod { //endregion Variable Declarations //region Constructors + /** + * Constructor for the {@link RandomDivorceMethod} class. + * Initializes the name and toolTipText variables using the specified name and toolTipText resources. + * + * @param name the name resource key used to retrieve the name from the resource bundle + * @param toolTipText the tooltip text resource key used to retrieve the tool tip text from the resource bundle + */ RandomDivorceMethod(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); + this.toolTipText = wordWrap(resources.getString(toolTipText)); } //endregion Constructors //region Getters + /** + * @return the tooltip text for the current object + */ public String getToolTipText() { return toolTipText; } //endregion Getters //region Boolean Comparisons + /** + * Checks if the current {@link RandomDivorceMethod} is {@code NONE}. + * + * @return {@code true} if the current {@link RandomDivorceMethod} is {@code NONE}, + * {@code false} otherwise. + */ public boolean isNone() { return this == NONE; } - public boolean isPercentage() { - return this == PERCENTAGE; + /** + * Checks if the current {@link RandomDivorceMethod} is {@code DICE_ROLL}. + * + * @return {@code true} if the current {@link RandomDivorceMethod} is {@code DICE_ROLL}, + * {@code false} otherwise. + */ + public boolean isDiceRoll() { + return this == DICE_ROLL; } //endregion Boolean Comparisons + /** + * @param options the {@link CampaignOptions} object used to initialize the {@link AbstractDivorce} instance + * @return an instance of {@link AbstractDivorce} based on the {@link RandomDivorceMethod} + */ public AbstractDivorce getMethod(final CampaignOptions options) { - switch (this) { - case PERCENTAGE: - return new PercentageRandomDivorce(options); - case NONE: - default: - return new DisabledRandomDivorce(options); + if (this == DICE_ROLL) { + return new RandomDivorce(options); + } else { + return new DisabledRandomDivorce(options); } } diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/RandomMarriageMethod.java b/MekHQ/src/mekhq/campaign/personnel/enums/RandomMarriageMethod.java index 6d926f07e0..aa9bd9d8dc 100644 --- a/MekHQ/src/mekhq/campaign/personnel/enums/RandomMarriageMethod.java +++ b/MekHQ/src/mekhq/campaign/personnel/enums/RandomMarriageMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -22,14 +22,23 @@ import mekhq.campaign.CampaignOptions; import mekhq.campaign.personnel.marriage.AbstractMarriage; import mekhq.campaign.personnel.marriage.DisabledRandomMarriage; -import mekhq.campaign.personnel.marriage.PercentageRandomMarriage; +import mekhq.campaign.personnel.marriage.RandomMarriage; import java.util.ResourceBundle; +import static megamek.client.ui.WrapLayout.wordWrap; + +/** + * The {@link RandomMarriageMethod} enum represents different methods of getting random marriages. + *

+ * The available methods are: + * - {@code NONE}: No random marriage method. + * - {@code DICE_ROLL}: Random marriage method using dice roll. + */ public enum RandomMarriageMethod { //region Enum Declarations NONE("RandomMarriageMethod.NONE.text", "RandomMarriageMethod.NONE.toolTipText"), - PERCENTAGE("RandomMarriageMethod.PERCENTAGE.text", "RandomMarriageMethod.PERCENTAGE.toolTipText"); + DICE_ROLL("RandomMarriageMethod.DICE_ROLL.text", "RandomMarriageMethod.DICE_ROLL.toolTipText"); //endregion Enum Declarations //region Variable Declarations @@ -37,15 +46,25 @@ public enum RandomMarriageMethod { private final String toolTipText; //endregion Variable Declarations + /** + * Constructor for the {@link RandomMarriageMethod} class. + * Initializes the name and toolTipText variables using the specified name and toolTipText resources. + * + * @param name the name resource key used to retrieve the name from the resource bundle + * @param toolTipText the tooltip text resource key used to retrieve the tool tip text from the resource bundle + */ //region Constructors RandomMarriageMethod(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); + this.toolTipText = wordWrap(resources.getString(toolTipText)); } //endregion Constructors + /** + * @return The tooltip text associated with the current instance of the class. + */ //region Getters public String getToolTipText() { return toolTipText; @@ -53,22 +72,36 @@ public String getToolTipText() { //endregion Getters //region Boolean Comparison Methods + /** + * Checks if the current {@link RandomMarriageMethod} is {@code NONE}. + * + * @return {@code true} if the current {@link RandomMarriageMethod} is {@code NONE}, + * {@code false} otherwise. + */ public boolean isNone() { return this == NONE; } - public boolean isPercentage() { - return this == PERCENTAGE; + /** + * Checks if the current {@link RandomMarriageMethod} is {@code DICE_ROLL}. + * + * @return {@code true} if the current {@link RandomMarriageMethod} is {@code DICE_ROLL}, + * {@code false} otherwise. + */ + public boolean isDiceRoll() { + return this == DICE_ROLL; } //endregion Boolean Comparison Methods + /** + * @param options the {@link CampaignOptions} object used to initialize the {@link AbstractMarriage} instance + * @return an instance of {@link AbstractMarriage} based on the {@link RandomMarriageMethod} + */ public AbstractMarriage getMethod(final CampaignOptions options) { - switch (this) { - case PERCENTAGE: - return new PercentageRandomMarriage(options); - case NONE: - default: - return new DisabledRandomMarriage(options); + if (this == DICE_ROLL) { + return new RandomMarriage(options); + } else { + return new DisabledRandomMarriage(options); } } diff --git a/MekHQ/src/mekhq/campaign/personnel/enums/RandomProcreationMethod.java b/MekHQ/src/mekhq/campaign/personnel/enums/RandomProcreationMethod.java index a5657132ed..2cef0e7441 100644 --- a/MekHQ/src/mekhq/campaign/personnel/enums/RandomProcreationMethod.java +++ b/MekHQ/src/mekhq/campaign/personnel/enums/RandomProcreationMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -22,14 +22,23 @@ import mekhq.campaign.CampaignOptions; import mekhq.campaign.personnel.procreation.AbstractProcreation; import mekhq.campaign.personnel.procreation.DisabledRandomProcreation; -import mekhq.campaign.personnel.procreation.PercentageRandomProcreation; +import mekhq.campaign.personnel.procreation.RandomProcreation; import java.util.ResourceBundle; +import static megamek.client.ui.WrapLayout.wordWrap; + +/** + * The {@link RandomProcreationMethod} enum represents different methods of getting random procreation. + *

+ * The available methods are: + * - {@code NONE}: No random procreation method. + * - {@code DICE_ROLL}: Random procreation method using dice roll. + */ public enum RandomProcreationMethod { //region Enum Declarations NONE("RandomProcreationMethod.NONE.text", "RandomProcreationMethod.NONE.toolTipText"), - PERCENTAGE("RandomProcreationMethod.PERCENTAGE.text", "RandomProcreationMethod.PERCENTAGE.toolTipText"); + DICE_ROLL("RandomProcreationMethod.DICE_ROLL.text", "RandomProcreationMethod.DICE_ROLL.toolTipText"); //endregion Enum Declarations //region Variable Declarations @@ -38,37 +47,61 @@ public enum RandomProcreationMethod { //endregion Variable Declarations //region Constructors + /** + * Constructor for the {@link RandomProcreationMethod} class. + * Initializes the name and toolTipText variables using the specified name and toolTipText resources. + * + * @param name the name resource key used to retrieve the name from the resource bundle + * @param toolTipText the tooltip text resource key used to retrieve the tool tip text from the resource bundle + */ RandomProcreationMethod(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); + this.toolTipText = wordWrap(resources.getString(toolTipText)); } //endregion Constructors //region Getters + /** + * @return The tooltip text associated with the current instance of the class. + */ public String getToolTipText() { return toolTipText; } //endregion Getters //region Boolean Comparison Methods + /** + * Checks if the current {@link RandomProcreationMethod} is {@code NONE}. + * + * @return {@code true} if the current {@link RandomProcreationMethod} is {@code NONE}, + * {@code false} otherwise. + */ public boolean isNone() { return this == NONE; } - public boolean isPercentage() { - return this == PERCENTAGE; + /** + * Checks if the current {@link RandomProcreationMethod} is {@code DICE_ROLL}. + * + * @return {@code true} if the current {@link RandomProcreationMethod} is {@code DICE_ROLL}, + * {@code false} otherwise. + */ + public boolean isDiceRoll() { + return this == DICE_ROLL; } //endregion Boolean Comparison Methods + /** + * @param options the {@link CampaignOptions} object used to initialize the {@link AbstractProcreation} instance + * @return an instance of {@link AbstractProcreation} based on the {@link RandomProcreationMethod} + */ public AbstractProcreation getMethod(final CampaignOptions options) { - switch (this) { - case PERCENTAGE: - return new PercentageRandomProcreation(options); - case NONE: - default: - return new DisabledRandomProcreation(options); + if (this == DICE_ROLL) { + return new RandomProcreation(options); + } else { + return new DisabledRandomProcreation(options); } } diff --git a/MekHQ/src/mekhq/campaign/personnel/familyTree/Genealogy.java b/MekHQ/src/mekhq/campaign/personnel/familyTree/Genealogy.java index 9ea88405ba..45f67982a7 100644 --- a/MekHQ/src/mekhq/campaign/personnel/familyTree/Genealogy.java +++ b/MekHQ/src/mekhq/campaign/personnel/familyTree/Genealogy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2020-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -38,6 +38,14 @@ import mekhq.campaign.personnel.enums.FamilialRelationshipType; import mekhq.io.idReferenceClasses.PersonIdReference; import mekhq.utilities.MHQXMLUtility; +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.*; +import java.util.stream.Collectors; /** * The Genealogy class is used to track immediate familial relationships, @@ -50,6 +58,7 @@ public class Genealogy { // region Variables private final Person origin; private Person spouse; + private Person originSpouse; // the person who originated the marriage private final List formerSpouses = new ArrayList<>(); private final Map> family = new HashMap<>(); // endregion Variables @@ -86,6 +95,20 @@ public void setSpouse(final @Nullable Person spouse) { this.spouse = spouse; } + /** + * @return the person who originated the marriage + */ + public @Nullable Person getOriginSpouse() { + return originSpouse; + } + + /** + * @param originSpouse the person who originated the marriage + */ + public void setOriginSpouse(final @Nullable Person originSpouse) { + this.originSpouse = originSpouse; + } + /** * @return a list of FormerSpouse objects for all the former spouses of the * current person @@ -209,6 +232,13 @@ public boolean hasChildren() { return getFamily().get(FamilialRelationshipType.CHILD) != null; } + /** + * @return true if the person has at least one kid, false otherwise + */ + public boolean hasNonAdultChildren(LocalDate localDate) { + return getChildren().stream().anyMatch(child -> child.isChild(localDate)); + } + /** * @return true if the Person has any parents, otherwise false */ diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/AbstractPersonnelGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/AbstractPersonnelGenerator.java index 2df7224d5a..4312bebd85 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/AbstractPersonnelGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/AbstractPersonnelGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 - The MegaMek Team. All Rights Reserved. + * Copyright (C) 2019-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,11 +18,8 @@ */ package mekhq.campaign.personnel.generator; -import java.time.LocalDate; -import java.util.Objects; - -import megamek.client.generator.RandomNameGenerator; import megamek.client.generator.RandomGenderGenerator; +import megamek.client.generator.RandomNameGenerator; import megamek.common.Compute; import megamek.common.enums.Gender; import mekhq.Utilities; @@ -33,6 +30,9 @@ import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.enums.Phenotype; +import java.time.LocalDate; +import java.util.Objects; + /** * Represents a class which can generate new {@link Person} objects * for a {@link Campaign}. @@ -95,11 +95,10 @@ protected Person createPerson(Campaign campaign) { /** * Generates an experience level for a {@link Person}. - * @param campaign The {@link Campaign} which tracks the person. * @param person The {@link Person} being generated. * @return An integer value between {@link SkillType#EXP_ULTRA_GREEN} and {@link SkillType#EXP_ELITE}. */ - protected int generateExperienceLevel(Campaign campaign, Person person) { + public int generateExperienceLevel(Person person) { int bonus = getSkillPreferences().getOverallRecruitBonus() + getSkillPreferences().getRecruitBonus(person.getPrimaryRole()); @@ -112,13 +111,20 @@ protected int generateExperienceLevel(Campaign campaign, Person person) { } /** - * Generates a name for a {@link Person}. - * @param campaign The {@link Campaign} to use to generate the person - * @param person The {@link Person} whose name is being generated. - * @param gender The person's gender, or a randomize value + * Generates and sets the name and gender of a person. + * + * @param campaign the campaign the person belongs to + * @param person the person whose name and gender is being generated + * @param gender the gender of the person. Can be Gender.MALE, Gender.FEMALE, Gender.NON_BINARY, or Gender.RANDOMIZE */ - protected void generateName(Campaign campaign, Person person, Gender gender) { - person.setGender((gender == Gender.RANDOMIZE) ? RandomGenderGenerator.generate() : gender); + protected void generateNameAndGender(Campaign campaign, Person person, Gender gender) { + int nonBinaryDiceSize = campaign.getCampaignOptions().getNonBinaryDiceSize(); + + if ((gender == Gender.RANDOMIZE) && (nonBinaryDiceSize > 0) && (Compute.randomInt(nonBinaryDiceSize) == 0)) { + person.setGender(RandomGenderGenerator.generateOther()); + } else { + person.setGender(RandomGenderGenerator.generate()); + } String factionCode = campaign.getCampaignOptions().isUseOriginFactionForNames() ? person.getOriginFaction().getShortName() diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java index f2a0f0ff1f..bd2c216742 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultPersonnelGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 MegaMek team + * Copyright (C) 2019-2024 MegaMek team * * This file is part of MekHQ. * @@ -21,10 +21,12 @@ import megamek.common.Compute; import megamek.common.enums.Gender; import mekhq.campaign.Campaign; +import mekhq.campaign.RandomSkillPreferences; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.backgrounds.BackgroundsController; import mekhq.campaign.personnel.education.EducationController; import mekhq.campaign.personnel.enums.PersonnelRole; +import mekhq.campaign.personnel.enums.education.EducationLevel; import mekhq.campaign.personnel.randomEvents.PersonalityController; import mekhq.campaign.universe.Faction; import mekhq.campaign.universe.Planet; @@ -76,7 +78,7 @@ public Person generate(Campaign campaign, PersonnelRole primaryRole, PersonnelRo person.setPrimaryRoleDirect(primaryRole); person.setSecondaryRoleDirect(secondaryRole); - int expLvl = generateExperienceLevel(campaign, person); + int expLvl = generateExperienceLevel(person); generateXp(campaign, person); @@ -84,31 +86,62 @@ public Person generate(Campaign campaign, PersonnelRole primaryRole, PersonnelRo generateBirthday(campaign, person, expLvl, person.isClanPersonnel() && !person.getPhenotype().isNone()); - if (expLvl == 0) { - person.setLoyalty(Compute.d6(3) + 2); - } else if (expLvl == 1) { - person.setLoyalty(Compute.d6(3) + 1); - } else { - person.setLoyalty(Compute.d6(3)); - } - AbstractSkillGenerator skillGenerator = new DefaultSkillGenerator(getSkillPreferences()); skillGenerator.generateSkills(campaign, person, expLvl); - AbstractSpecialAbilityGenerator specialAbilityGenerator = new DefaultSpecialAbilityGenerator(); - specialAbilityGenerator.setSkillPreferences(getSkillPreferences()); - specialAbilityGenerator.generateSpecialAbilities(campaign, person, expLvl); + // Limit skills by age for children and adolescents + int age = person.getAge(campaign.getLocalDate()); + + if (age < 16) { + person.removeAllSkills(); + // regenerate expLvl to factor in skill changes from age + expLvl = generateExperienceLevel(person); + } else if (age < 18) { + person.limitSkills(1); + + expLvl = generateExperienceLevel(person); + } + + // set SPAs + if (expLvl >= 0) { + AbstractSpecialAbilityGenerator specialAbilityGenerator = new DefaultSpecialAbilityGenerator(); + specialAbilityGenerator.setSkillPreferences(new RandomSkillPreferences()); + specialAbilityGenerator.generateSpecialAbilities(campaign, person, expLvl); + } + + // set interest in marriage and children flags + int interestInMarriageDiceSize = campaign.getCampaignOptions().getNoInterestInMarriageDiceSize(); + person.setMarriageable(((interestInMarriageDiceSize != 0) && (Compute.randomInt(interestInMarriageDiceSize)) != 0)); + + int interestInChildren = campaign.getCampaignOptions().getNoInterestInChildrenDiceSize(); + person.setTryingToConceive(((interestInChildren != 0) && (Compute.randomInt(interestInChildren)) != 0)); // Do naming at the end, to ensure the keys are set - generateName(campaign, person, gender); + generateNameAndGender(campaign, person, gender); //check for Bloodname campaign.checkBloodnameAdd(person, false); person.setDaysToWaitForHealing(campaign.getCampaignOptions().getNaturalHealingWaitingPeriod()); - // set education - EducationController.setInitialEducation(campaign, person); + // set loyalty + if (expLvl <= 0) { + person.setLoyalty(Compute.d6(3) + 2); + } else if (expLvl == 1) { + person.setLoyalty(Compute.d6(3) + 1); + } else { + person.setLoyalty(Compute.d6(3)); + } + + // set education based on age + if (age < 16) { + person.setEduHighestEducation(EducationLevel.EARLY_CHILDHOOD); + } else { + person.setEduHighestEducation(EducationLevel.HIGH_SCHOOL); + } + + // generate personality + PersonalityController.generatePersonality(person); // generate background BackgroundsController.generateBackground(campaign, person); diff --git a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java index b7dc5f1d4d..4f1bde8121 100644 --- a/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java +++ b/MekHQ/src/mekhq/campaign/personnel/generator/DefaultSkillGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2021 - The MegaMek Team. All Rights Reserved. + * Copyright (C) 2019-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -28,7 +28,6 @@ import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; public class DefaultSkillGenerator extends AbstractSkillGenerator { //region Constructors @@ -62,8 +61,13 @@ public void generateSkills(final Campaign campaign, final Person person, final i // roll small arms skill if (!person.getSkills().hasSkill(SkillType.S_SMALL_ARMS)) { int sarmsLvl = Utilities.generateExpLevel( - (primaryRole.isSupport() || secondaryRole.isSupport(true)) + (primaryRole.isSupport(true) || secondaryRole.isSupport(true)) ? rskillPrefs.getSupportSmallArmsBonus() : rskillPrefs.getCombatSmallArmsBonus()); + + if (primaryRole.isCivilian()) { + sarmsLvl = 0; + } + if (sarmsLvl > SkillType.EXP_ULTRA_GREEN) { addSkill(person, SkillType.S_SMALL_ARMS, sarmsLvl, rskillPrefs.randomizeSkill(), bonus); } @@ -100,7 +104,7 @@ public void generateSkills(final Campaign campaign, final Person person, final i if (Utilities.rollProbability(rskillPrefs.getSecondSkillProb())) { final List possibleSkills = Arrays.stream(SkillType.skillList) .filter(stype -> !person.getSkills().hasSkill(stype)) - .collect(Collectors.toList()); + .toList(); String selSkill = possibleSkills.get(Compute.randomInt(possibleSkills.size())); int secondLvl = Utilities.generateExpLevel(rskillPrefs.getSecondSkillBonus()); addSkill(person, selSkill, secondLvl, rskillPrefs.randomizeSkill(), bonus); diff --git a/MekHQ/src/mekhq/campaign/personnel/marriage/AbstractMarriage.java b/MekHQ/src/mekhq/campaign/personnel/marriage/AbstractMarriage.java index 3dad3f772b..536434303c 100644 --- a/MekHQ/src/mekhq/campaign/personnel/marriage/AbstractMarriage.java +++ b/MekHQ/src/mekhq/campaign/personnel/marriage/AbstractMarriage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,6 +18,7 @@ */ package mekhq.campaign.personnel.marriage; +import megamek.client.generator.RandomGenderGenerator; import megamek.common.Compute; import megamek.common.annotations.Nullable; import megamek.common.enums.Gender; @@ -28,9 +29,11 @@ import mekhq.campaign.log.PersonalLogger; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.enums.MergingSurnameStyle; +import mekhq.campaign.personnel.enums.PrisonerStatus; import mekhq.campaign.personnel.enums.RandomMarriageMethod; import java.time.LocalDate; +import java.util.ArrayList; import java.util.List; import java.util.ResourceBundle; @@ -44,7 +47,6 @@ public abstract class AbstractMarriage { private final RandomMarriageMethod method; private boolean useClanPersonnelMarriages; private boolean usePrisonerMarriages; - private boolean useRandomSameSexMarriages; private boolean useRandomClanPersonnelMarriages; private boolean useRandomPrisonerMarriages; @@ -57,7 +59,6 @@ protected AbstractMarriage(final RandomMarriageMethod method, final CampaignOpti this.method = method; setUseClanPersonnelMarriages(options.isUseClanPersonnelMarriages()); setUsePrisonerMarriages(options.isUsePrisonerMarriages()); - setUseRandomSameSexMarriages(options.isUseRandomSameSexMarriages()); setUseRandomClanPersonnelMarriages(options.isUseRandomClanPersonnelMarriages()); setUseRandomPrisonerMarriages(options.isUseRandomPrisonerMarriages()); } @@ -84,14 +85,6 @@ public void setUsePrisonerMarriages(final boolean usePrisonerMarriages) { this.usePrisonerMarriages = usePrisonerMarriages; } - public boolean isUseRandomSameSexMarriages() { - return useRandomSameSexMarriages; - } - - public void setUseRandomSameSexMarriages(final boolean useRandomSameSexMarriages) { - this.useRandomSameSexMarriages = useRandomSameSexMarriages; - } - public boolean isUseRandomClanPersonnelMarriages() { return useRandomClanPersonnelMarriages; } @@ -111,14 +104,12 @@ public void setUseRandomPrisonerMarriages(final boolean useRandomPrisonerMarriag /** * This is used to determine if a person can marry - * @param campaign the campaign the person is a part of * @param today the current date * @param person the person to determine for * @param randomMarriage if this is for random marriage or manual marriage * @return null if they can, otherwise the reason why they cannot */ - public @Nullable String canMarry(final Campaign campaign, final LocalDate today, - final Person person, final boolean randomMarriage) { + public @Nullable String canMarry(final LocalDate today, final Person person, final boolean randomMarriage) { if (!person.isMarriageable()) { return resources.getString("cannotMarry.NotMarriageable.text"); } else if (person.getGenealogy().hasSpouse()) { @@ -127,7 +118,7 @@ public void setUseRandomPrisonerMarriages(final boolean useRandomPrisonerMarriag return resources.getString("cannotMarry.Inactive.text"); } else if (person.isDeployed()) { return resources.getString("cannotMarry.Deployed.text"); - } else if (person.getAge(today) < campaign.getCampaignOptions().getMinimumMarriageAge()) { + } else if (person.isChild(today)) { return resources.getString("cannotMarry.TooYoung.text"); } else if (!isUseClanPersonnelMarriages() && person.isClanPersonnel()) { return resources.getString("cannotMarry.ClanPersonnel.text"); @@ -164,7 +155,7 @@ public boolean safeSpouse(final Campaign campaign, final LocalDate today, final // marriages. if (person.equals(potentialSpouse) - || (canMarry(campaign, today, potentialSpouse, randomMarriage) != null) + || (canMarry(today, potentialSpouse, randomMarriage) != null) || person.getGenealogy().checkMutualAncestors(potentialSpouse, campaign.getCampaignOptions().getCheckMutualAncestorsDepth())) { return false; @@ -182,13 +173,35 @@ public boolean safeSpouse(final Campaign campaign, final LocalDate today, final * @param origin the origin person being married * @param spouse the person's spouse, which can be null if no marriage is to occur * @param surnameStyle the style for how the two people's surnames will change as part of the marriage + * @param isBackground whether the marriage occurred as part of a character's background */ public void marry(final Campaign campaign, final LocalDate today, final Person origin, - final @Nullable Person spouse, final MergingSurnameStyle surnameStyle) { + final @Nullable Person spouse, final MergingSurnameStyle surnameStyle, + boolean isBackground) { if (spouse == null) { return; } + performMarriageChanges(campaign, today, origin, spouse, surnameStyle, isBackground); + + // And finally, we trigger person changed events + MekHQ.triggerEvent(new PersonChangedEvent(origin)); + MekHQ.triggerEvent(new PersonChangedEvent(spouse)); + } + + /** + * Updates the necessary information to perform a marriage between two individuals. + * + * @param campaign the campaign in which the marriage is taking place + * @param today the current date of the marriage + * @param origin the first person getting married + * @param spouse the second person getting married + * @param surnameStyle the style of surname changes to be applied + * @param isBackground whether the marriage occurred as part of a character's background + */ + public static void performMarriageChanges(Campaign campaign, LocalDate today, Person origin, + Person spouse, MergingSurnameStyle surnameStyle, + boolean isBackground) { // Immediately set both Maiden Names, to avoid any divorce bugs (as the default is now an empty string) origin.setMaidenName(origin.getSurname()); spouse.setMaidenName(spouse.getSurname()); @@ -204,16 +217,34 @@ public void marry(final Campaign campaign, final LocalDate today, final Person o PersonalLogger.marriage(origin, spouse, today); PersonalLogger.marriage(spouse, origin, today); - campaign.addReport(String.format(resources.getString("marriage.report"), origin.getHyperlinkedName(), - spouse.getHyperlinkedName())); + if (!isBackground) { + campaign.addReport(String.format(resources.getString("marriage.report"), + origin.getHyperlinkedName(), + spouse.getHyperlinkedName())); + + // Process the loyalty change + if (campaign.getCampaignOptions().isUseLoyaltyModifiers()) { + origin.performRandomizedLoyaltyChange(campaign, false, true); + spouse.performRandomizedLoyaltyChange(campaign, false, true); + } + } + + // log the origin spouse for both partners + origin.getGenealogy().setOriginSpouse(origin); + spouse.getGenealogy().setOriginSpouse(origin); + + // recruit the spouse if they're not already in the unit + if ((!isBackground) && (spouse.getJoinedCampaign() == null)) { + campaign.recruitPerson(spouse, PrisonerStatus.FREE, true, false); + + ResourceBundle recruitmentResources = ResourceBundle.getBundle("mekhq.resources.Campaign", + MekHQ.getMHQOptions().getLocale()); - // Process the loyalty change - if (campaign.getCampaignOptions().isUseLoyaltyModifiers()) { - origin.performRandomizedLoyaltyChange(campaign, false, true); - spouse.performRandomizedLoyaltyChange(campaign, false, true); + campaign.addReport(String.format(recruitmentResources.getString("dependentJoinsForce.text"), + spouse.getHyperlinkedFullTitle())); } - // And finally we trigger person changed events + // And finally, we trigger person changed events MekHQ.triggerEvent(new PersonChangedEvent(origin)); MekHQ.triggerEvent(new PersonChangedEvent(spouse)); } @@ -225,50 +256,147 @@ public void marry(final Campaign campaign, final LocalDate today, final Person o * @param today the current day * @param person the person to process */ - public void processNewDay(final Campaign campaign, final LocalDate today, final Person person) { - if (canMarry(campaign, today, person, true) != null) { + public void processNewWeek(final Campaign campaign, final LocalDate today, final Person person) { + if (canMarry(today, person, true) != null) { return; } - if (randomOppositeSexMarriage(person)) { - marryRandomSpouse(campaign, today, person, false); - } else if (isUseRandomSameSexMarriages() && randomSameSexMarriage(person)) { - marryRandomSpouse(campaign, today, person, true); + if (randomMarriage()) { + boolean isSameSex = false; + + int sameSexDiceSize = campaign.getCampaignOptions().getRandomSameSexMarriageDiceSize(); + + if (sameSexDiceSize == 1) { + isSameSex = true; + } else if ((sameSexDiceSize != 0) && (Compute.randomInt(sameSexDiceSize) == 0)) { + isSameSex = true; + } + + boolean isInterUnit = false; + + int interUnitDiceSize = campaign.getCampaignOptions().getRandomNewDependentMarriage(); + + if (interUnitDiceSize == 1) { + isInterUnit = true; + } else if ((interUnitDiceSize != 0) && (Compute.randomInt(interUnitDiceSize) == 0)) { + isInterUnit = true; + } + + marryRandomSpouse(campaign, today, person, isSameSex, isInterUnit, true); + } + } + + /** + * This method is used to check for marriages that occurred in a character's background + * + * @param campaign the campaign for which to process the marriage rolls + * @param today the current date + * @param person the person for whom to process the marriage rolls + */ + public void processBackgroundMarriageRolls(final Campaign campaign, final LocalDate today, final Person person) { + if (canMarry(today, person, true) != null) { + return; + } + + if (randomMarriage()) { + boolean isSameSex = false; + + int sameSexDiceSize = campaign.getCampaignOptions().getRandomSameSexMarriageDiceSize(); + + if (sameSexDiceSize == 1) { + isSameSex = true; + } else if ((sameSexDiceSize != 0) && (Compute.randomInt(sameSexDiceSize) == 0)) { + isSameSex = true; + } + + marryRandomSpouse(campaign, today, person, isSameSex, false, true); } } //region Random Marriage /** * This determines if a person will randomly marry an opposite sex spouse. - * @param person the person to determine if they are getting randomly married * @return true if the person is to randomly marry */ - protected abstract boolean randomOppositeSexMarriage(final Person person); + protected abstract boolean randomMarriage(); /** - * This determines if a person will randomly marry a same-sex spouse. - * @param person the person who may be randomly marrying a same-sex spouse - * @return true if the person is to randomly marry a same-sex spouse + * This finds a random spouse and marries them to the provided person. + * + * @param campaign the campaign the person is a part of + * @param today the current date + * @param person the person who is getting randomly married + * @param sameSex whether the marriage is between same-sex partners + * @param isInterUnit whether the marriage is to another character chosen from among potential partners already in the campaign unit. + * @param isBackground whether the marriage occurred in a character's background */ - protected abstract boolean randomSameSexMarriage(final Person person); + protected void marryRandomSpouse(final Campaign campaign, final LocalDate today, + final Person person, final boolean sameSex, + boolean isInterUnit, boolean isBackground) { + Gender personGender = person.getGender(); + + boolean isNonBinary = (campaign.getCampaignOptions().getNonBinaryDiceSize() > 0) + && (Compute.randomInt(campaign.getCampaignOptions().getNonBinaryDiceSize()) == 0); + + Gender spouseGender = switch (personGender) { + case MALE, OTHER_MALE -> sameSex ? (isNonBinary ? + Gender.OTHER_MALE : Gender.MALE) : (isNonBinary ? Gender.OTHER_FEMALE : Gender.FEMALE); + case FEMALE, OTHER_FEMALE -> sameSex ? (isNonBinary ? + Gender.OTHER_FEMALE : Gender.FEMALE) : (isNonBinary ? Gender.OTHER_MALE : Gender.MALE); + case RANDOMIZE -> RandomGenderGenerator.generate(); + }; + + List potentialSpouses = new ArrayList<>(); + Person spouse = null; + + if (isInterUnit) { + potentialSpouses = campaign.getActivePersonnel().stream() + .filter(potentialSpouse -> isPotentialRandomSpouse(campaign, today, person, potentialSpouse, spouseGender)) + .toList(); + + if (!potentialSpouses.isEmpty()) { + spouse = potentialSpouses.get(Compute.randomInt(potentialSpouses.size())); + } + } + + if ((!isInterUnit) || (potentialSpouses.isEmpty())) { + spouse = createExternalSpouse(campaign, today, person, spouseGender); + } + + marry(campaign, today, person, spouse, MergingSurnameStyle.WEIGHTED, isBackground); + } /** - * This finds a random spouse and marries them to the provided person. + * Creates a spouse for the given person. + * * @param campaign the campaign the person is a part of * @param today the current date - * @param person the person who is getting randomly married - * @param sameSex whether the marriage is homosexual or heterosexual + * @param person the person for whom the external spouse is being created + * @param gender the gender of the external spouse + * @return the created external spouse */ - protected void marryRandomSpouse(final Campaign campaign, final LocalDate today, - final Person person, final boolean sameSex) { - final Gender gender = sameSex ? person.getGender() : (person.getGender().isMale() ? Gender.FEMALE : Gender.MALE); - final List potentials = campaign.getActivePersonnel().stream() - .filter(potentialSpouse -> isPotentialRandomSpouse(campaign, today, person, potentialSpouse, gender)) - .toList(); - if (!potentials.isEmpty()) { - marry(campaign, today, person, potentials.get(Compute.randomInt(potentials.size())), - MergingSurnameStyle.WEIGHTED); + Person createExternalSpouse(final Campaign campaign, final LocalDate today, final Person person, Gender gender) { + Person externalSpouse = campaign.newDependent(false, gender); + + + // Calculate person's age and the maximum and minimum allowable spouse ages + int personAge = person.getAge(today); + int externalSpouseAge = externalSpouse.getAge(today); + int maximumAgeDifference = campaign.getCampaignOptions().getRandomMarriageAgeRange(); + int externalSpouseMinAge = Math.max (18, personAge - maximumAgeDifference); + int externalSpouseMaxAge = personAge + maximumAgeDifference; + + if (externalSpouseAge < externalSpouseMinAge) { + int difference = externalSpouseMinAge - externalSpouseAge; + + externalSpouse.setBirthday(externalSpouse.getBirthday().minusYears(difference)); + } else if (externalSpouseAge > externalSpouseMaxAge) { + int difference = externalSpouseMaxAge - externalSpouseAge; + + externalSpouse.setBirthday(externalSpouse.getBirthday().plusYears(difference)); } + + return externalSpouse; } /** diff --git a/MekHQ/src/mekhq/campaign/personnel/marriage/DisabledRandomMarriage.java b/MekHQ/src/mekhq/campaign/personnel/marriage/DisabledRandomMarriage.java index c2bacf8472..c54825bffd 100644 --- a/MekHQ/src/mekhq/campaign/personnel/marriage/DisabledRandomMarriage.java +++ b/MekHQ/src/mekhq/campaign/personnel/marriage/DisabledRandomMarriage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -19,7 +19,6 @@ package mekhq.campaign.personnel.marriage; import mekhq.campaign.CampaignOptions; -import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.enums.RandomMarriageMethod; public class DisabledRandomMarriage extends AbstractMarriage { @@ -30,12 +29,7 @@ public DisabledRandomMarriage(final CampaignOptions options) { //endregion Constructors @Override - protected boolean randomOppositeSexMarriage(final Person person) { - return false; - } - - @Override - protected boolean randomSameSexMarriage(final Person person) { + protected boolean randomMarriage() { return false; } } diff --git a/MekHQ/src/mekhq/campaign/personnel/marriage/PercentageRandomMarriage.java b/MekHQ/src/mekhq/campaign/personnel/marriage/PercentageRandomMarriage.java deleted file mode 100644 index f36c5f2218..0000000000 --- a/MekHQ/src/mekhq/campaign/personnel/marriage/PercentageRandomMarriage.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2021 - 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.marriage; - -import megamek.common.Compute; -import mekhq.campaign.CampaignOptions; -import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.enums.RandomMarriageMethod; - -public class PercentageRandomMarriage extends AbstractMarriage { - //region Variable Declarations - private double oppositeSexPercentage; - private double sameSexPercentage; - //endregion Variable Declarations - - //region Constructors - public PercentageRandomMarriage(final CampaignOptions options) { - super(RandomMarriageMethod.PERCENTAGE, options); - setOppositeSexPercentage(options.getPercentageRandomMarriageOppositeSexChance()); - setSameSexPercentage(options.getPercentageRandomMarriageSameSexChance()); - } - //endregion Constructors - - //region Getters/Setters - public double getOppositeSexPercentage() { - return oppositeSexPercentage; - } - - public void setOppositeSexPercentage(final double oppositeSexPercentage) { - this.oppositeSexPercentage = oppositeSexPercentage; - } - - public double getSameSexPercentage() { - return sameSexPercentage; - } - - public void setSameSexPercentage(final double sameSexPercentage) { - this.sameSexPercentage = sameSexPercentage; - } - //endregion Getters/Setters - - @Override - protected boolean randomOppositeSexMarriage(final Person person) { - return Compute.randomFloat() < getOppositeSexPercentage(); - } - - @Override - protected boolean randomSameSexMarriage(final Person person) { - return Compute.randomFloat() < getSameSexPercentage(); - } -} diff --git a/MekHQ/src/mekhq/campaign/personnel/marriage/RandomMarriage.java b/MekHQ/src/mekhq/campaign/personnel/marriage/RandomMarriage.java new file mode 100644 index 0000000000..dbb82764c0 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/personnel/marriage/RandomMarriage.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2021-2024 - 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.marriage; + +import megamek.common.Compute; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.personnel.enums.RandomMarriageMethod; + +/** + * {@link RandomMarriage} class represents a type of marriage where the result is determined by + * rolling a die. + * It extends the {@link AbstractMarriage} class. + */ +public class RandomMarriage extends AbstractMarriage { + //region Variable Declarations + private int marriageDiceSize; + //endregion Variable Declarations + + //region Constructors + /** + * Constructs a {@link RandomMarriage} object. This object is used to manage randomly determined + * marriages. + * + * @param options the {@link CampaignOptions} object that contains current game campaign settings. + */ + public RandomMarriage(final CampaignOptions options) { + super(RandomMarriageMethod.DICE_ROLL, options); + + setMarriageDiceSize(options.getRandomMarriageDiceSize()); + } + //endregion Constructors + + //region Getters/Setters + /** + * Sets the size of the marriage dice used in a random marriage. + * + * @param marriageDiceSize the size of the marriage dice to set + */ + public void setMarriageDiceSize(final int marriageDiceSize) { + this.marriageDiceSize = marriageDiceSize; + } + //endregion Getters/Setters + + @Override + protected boolean randomMarriage() { + if (marriageDiceSize == 0) { + return false; + } else if (marriageDiceSize == 1) { + return true; + } + + return Compute.randomInt(marriageDiceSize) == 0; + } +} diff --git a/MekHQ/src/mekhq/campaign/personnel/procreation/AbstractProcreation.java b/MekHQ/src/mekhq/campaign/personnel/procreation/AbstractProcreation.java index d72ac2c824..7bcf080005 100644 --- a/MekHQ/src/mekhq/campaign/personnel/procreation/AbstractProcreation.java +++ b/MekHQ/src/mekhq/campaign/personnel/procreation/AbstractProcreation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,6 +21,8 @@ import megamek.codeUtilities.MathUtility; import megamek.common.Compute; import megamek.common.annotations.Nullable; +import megamek.common.enums.Gender; +import megamek.common.options.IOption; import mekhq.MHQConstants; import mekhq.MekHQ; import mekhq.campaign.Campaign; @@ -30,14 +32,14 @@ import mekhq.campaign.log.MedicalLogger; import mekhq.campaign.log.PersonalLogger; import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.PersonnelOptions; import mekhq.campaign.personnel.education.EducationController; import mekhq.campaign.personnel.enums.*; import mekhq.campaign.personnel.enums.education.EducationLevel; import java.time.LocalDate; import java.time.temporal.ChronoUnit; -import java.util.ResourceBundle; -import java.util.UUID; +import java.util.*; /** * AbstractProcreation is the baseline class for procreation and birth in MekHQ. It holds all the @@ -121,7 +123,7 @@ public void setUseRandomPrisonerProcreation(final boolean useRandomPrisonerProcr /** * This method determines the number of babies a person will give birth to. * @param multiplePregnancyOccurrences the X occurrences for there to be a single multiple - * child occurrence (i.e. 1 in X) + * child occurrence (i.e., 1 in X) * @return the number of babies the person will give birth to, limited to decuplets */ protected int determineNumberOfBabies(final int multiplePregnancyOccurrences) { @@ -146,8 +148,8 @@ private int determinePregnancyDuration() { // pregnancy duration to create a randomized pregnancy duration final double gaussian = Math.sqrt(-2.0 * Math.log(Math.random())) * Math.cos(2.0 * Math.PI * Math.random()); - // To not get weird results, we limit the variance to +/- 4.0 (almost 6 weeks). A base - // length of 268 creates a solid enough duration for now. + // To not get unusual results, we limit the variance to +/- 4.0 (almost 6 weeks). + // A base length of 268 creates a solid enough duration for now. return 268 + (int) Math.round(MathUtility.clamp(gaussian, -4d, 4d) * 10.0); } @@ -240,38 +242,54 @@ public int determinePregnancyWeek(final LocalDate today, final Person person) { * @param campaign the campaign the person is a part of * @param today the current date * @param mother the newly pregnant mother + * @param isNoReport true if no message should be posted to the daily report */ - public void addPregnancy(final Campaign campaign, final LocalDate today, final Person mother) { - addPregnancy(campaign, today, mother, determineNumberOfBabies( - campaign.getCampaignOptions().getMultiplePregnancyOccurrences())); + public void addPregnancy(final Campaign campaign, final LocalDate today, final Person mother, boolean isNoReport) { + addPregnancy( + campaign, + today, + mother, + determineNumberOfBabies(campaign.getCampaignOptions().getMultiplePregnancyOccurrences()), + isNoReport + ); } /** * This method is how a person becomes pregnant with the specified number of children. They have - * their due date set and the parentage of the pregnancy determined. + * their due date set, and the parentage of the pregnancy is determined. * * @param campaign the campaign the person is a part of * @param today the current date * @param mother the newly pregnant mother * @param size the number of children the mother is having + * @param isNoReport true if no message should be posted to the daily report */ public void addPregnancy(final Campaign campaign, final LocalDate today, final Person mother, - final int size) { + final int size, boolean isNoReport) { if (size < 1) { return; } mother.setExpectedDueDate(today.plusDays(MHQConstants.PREGNANCY_STANDARD_DURATION)); + mother.setDueDate(today.plusDays(determinePregnancyDuration())); + mother.getExtraData().set(PREGNANCY_CHILDREN_DATA, size); + mother.getExtraData().set(PREGNANCY_FATHER_DATA, mother.getGenealogy().hasSpouse() ? mother.getGenealogy().getSpouse().getId().toString() : null); final String babyAmount = resources.getString("babyAmount.text").split(",")[size - 1]; - campaign.addReport(String.format(resources.getString("babyConceived.report"), - mother.getHyperlinkedName(), babyAmount).trim()); + + if (!isNoReport) { + campaign.addReport(String.format(resources.getString("babyConceived.report"), + mother.getHyperlinkedName(), + babyAmount).trim()); + } + if (campaign.getCampaignOptions().isLogProcreation()) { MedicalLogger.hasConceived(mother, today, babyAmount); + if (mother.getGenealogy().hasSpouse()) { PersonalLogger.spouseConceived(mother.getGenealogy().getSpouse(), mother.getFullName(), today, babyAmount); @@ -318,8 +336,8 @@ public void birth(final Campaign campaign, final LocalDate today, final Person m // Create Babies for (int i = 0; i < size; i++) { - // Create the specific baby - final Person baby = campaign.newDependent(true); + // Create a baby + final Person baby = campaign.newDependent(true, Gender.RANDOMIZE); baby.setSurname(campaign.getCampaignOptions().getBabySurnameStyle() .generateBabySurname(mother, father, baby.getGender())); baby.setBirthday(today); @@ -328,20 +346,7 @@ public void birth(final Campaign campaign, final LocalDate today, final Person m campaign.addReport(String.format(resources.getString("babyBorn.report"), mother.getHyperlinkedName(), baby.getHyperlinkedName(), GenderDescriptors.BOY_GIRL.getDescriptor(baby.getGender()))); - if (campaign.getCampaignOptions().isLogProcreation()) { - MedicalLogger.deliveredBaby(mother, baby, today); - if (father != null) { - PersonalLogger.ourChildBorn(father, baby, mother.getFullName(), today); - } - } - - // Create genealogy information - baby.getGenealogy().addFamilyMember(FamilialRelationshipType.PARENT, mother); - mother.getGenealogy().addFamilyMember(FamilialRelationshipType.CHILD, baby); - if (father != null) { - baby.getGenealogy().addFamilyMember(FamilialRelationshipType.PARENT, father); - father.getGenealogy().addFamilyMember(FamilialRelationshipType.CHILD, baby); - } + logAndUpdateFamily(campaign, today, mother, baby, father); // Founder Tag Assignment if (campaign.getCampaignOptions().isAssignNonPrisonerBabiesFounderTag() @@ -376,8 +381,93 @@ public void birth(final Campaign campaign, final LocalDate today, final Person m mother.performRandomizedLoyaltyChange(campaign, false, true); + // check desire for children + if (Compute.d6(1) <= 2) { + mother.setTryingToConceive(false); + } + + // Cleanup Data + removePregnancy(mother); + } + + /** + * Logs the birth of a baby and updates the genealogy information of the family. + * + * @param campaign the ongoing campaign + * @param today the current date + * @param mother the mother of the baby + * @param baby the newborn baby + * @param father the father of the baby, null if unknown + */ + private static void logAndUpdateFamily(Campaign campaign, LocalDate today, Person mother, Person baby, Person father) { + if (campaign.getCampaignOptions().isLogProcreation()) { + MedicalLogger.deliveredBaby(mother, baby, today); + if (father != null) { + PersonalLogger.ourChildBorn(father, baby, mother.getFullName(), today); + } + } + + // Create genealogy information + baby.getGenealogy().addFamilyMember(FamilialRelationshipType.PARENT, mother); + mother.getGenealogy().addFamilyMember(FamilialRelationshipType.CHILD, baby); + + if (father != null) { + baby.getGenealogy().addFamilyMember(FamilialRelationshipType.PARENT, father); + father.getGenealogy().addFamilyMember(FamilialRelationshipType.CHILD, baby); + } + } + + /** + * Creates baby/babies and performs any necessary operations such as setting birthdate, creating reports, + * updating genealogy, setting education, loyalty, personality, and recruiting the baby. + * This version is for historic births that occur as part of a character's background. + * + * @param campaign the campaign object + * @param today the current date + * @param mother the mother person object + * @param father the father person object, can be null if the father is unknown + * @return the babies + */ + public List birthHistoric(final Campaign campaign, final LocalDate today, final Person mother, @Nullable final Person father) { + List babies = new ArrayList<>(); + + // Determine the number of children + final int size = mother.getExtraData().get(PREGNANCY_CHILDREN_DATA, 1); + // Create Babies + for (int i = 0; i < size; i++) { + // Create the babies + final Person baby = campaign.newDependent(true, Gender.RANDOMIZE); + + baby.setSurname(campaign.getCampaignOptions().getBabySurnameStyle() + .generateBabySurname(mother, father, baby.getGender())); + + baby.setBirthday(today);// Limit skills by age for children and adolescents + + baby.removeAllSkills(); + + // re-roll SPAs to include in any age and skill adjustments + Enumeration options = new PersonnelOptions().getOptions(PersonnelOptions.LVL3_ADVANTAGES); + + for (IOption option : Collections.list(options)) { + baby.getOptions().getOption(option.getName()).clearValue(); + } + + baby.setLoyalty(Compute.d6(3) + 2); + + // set education based on age + baby.setEduHighestEducation(EducationLevel.EARLY_CHILDHOOD); + + // Create reports and log the birth + logAndUpdateFamily(campaign, today, mother, baby, father); + + // add to the list of babies + babies.add(baby); + } + // Cleanup Data removePregnancy(mother); + + return babies; } /** @@ -390,35 +480,47 @@ public void processPregnancyComplications(final Campaign campaign, final LocalDa final Person person) { // The child might be able to be born, albeit into a world without their mother. // The status, however, can be manually set for males and for those who are not pregnant. - // This is purposeful, to allow for player customization, and thus we first check if they + // This is purposeful to allow for player customization, and thus we first check if they // are pregnant before checking if the birth occurs if (!person.isPregnant()) { return; } final int pregnancyWeek = determinePregnancyWeek(today, person); - final double babyBornChance; - if (pregnancyWeek > 35) { - babyBornChance = 0.99; - } else if (pregnancyWeek > 29) { - babyBornChance = 0.95; - } else if (pregnancyWeek > 25) { - babyBornChance = 0.9; - } else if (pregnancyWeek == 25) { - babyBornChance = 0.8; - } else if (pregnancyWeek == 24) { - babyBornChance = 0.5; - } else if (pregnancyWeek == 23) { - babyBornChance = 0.25; - } else { - babyBornChance = 0.0; - } + final double babyBornChance = getBabyBornChance(pregnancyWeek); if (Compute.randomFloat() < babyBornChance) { birth(campaign, today, person); } } + /** + * Calculates the chance of a baby being born based on the pregnancy week. + * + * @param pregnancyWeek the week of the pregnancy + * @return the chance of a baby being born, ranging from 0.0 to 1.0 + */ + private static double getBabyBornChance(int pregnancyWeek) { + int range = switch (pregnancyWeek) { + case 23 -> 1; + case 24 -> 2; + case 25 -> 3; + default -> (pregnancyWeek > 25 && pregnancyWeek <= 29) ? 4 : + (pregnancyWeek > 29 && pregnancyWeek <=35) ? 5 : + (pregnancyWeek > 35) ? 6 : 0; + }; + + return switch (range) { + case 1 -> 0.25; + case 2 -> 0.5; + case 3 -> 0.8; + case 4 -> 0.9; + case 5 -> 0.95; + case 6 -> 0.99; + default -> 0.0; + }; + } + //region New Day /** * Process new day procreation for an individual @@ -426,7 +528,7 @@ public void processPregnancyComplications(final Campaign campaign, final LocalDa * @param today the current day * @param person the person to process */ - public void processNewDay(final Campaign campaign, final LocalDate today, final Person person) { + public void processNewWeek(final Campaign campaign, final LocalDate today, final Person person) { // Instantly return for male personnel if (person.getGender().isMale()) { return; @@ -434,22 +536,36 @@ public void processNewDay(final Campaign campaign, final LocalDate today, final // Check if they are already pregnant if (person.isPregnant()) { - // They give birth if the due date is the current day - if (today.isEqual(person.getDueDate())) { + // They give birth if the due date has passed + if ((today.isAfter(person.getDueDate())) || (today.isEqual(person.getDueDate()))) { birth(campaign, today, person); } return; } // Make the required checks for random procreation + processRandomProcreationCheck(campaign, today, person, false); + } + + /** + * Checks if a person randomly procreates on the given day in the campaign. + * If the person does procreate, add a pregnancy to the campaign for the person. + * + * @param campaign The campaign to check procreation for. + * @param today The current date. + * @param person The person to check for procreation. + * @param isNoReport true, if the player shouldn't be informed, otherwise false + */ + public void processRandomProcreationCheck(final Campaign campaign, final LocalDate today, + final Person person, boolean isNoReport) { if (randomlyProcreates(today, person)) { - addPregnancy(campaign, today, person); + addPregnancy(campaign, today, person, isNoReport); } } //region Random Procreation /** - * Determines if a non-pregnant female person procreates on a given day + * Determines if a non-pregnant woman procreates on a given day * @param today the current day * @param person the person in question * @return true if they do, otherwise false @@ -457,12 +573,8 @@ public void processNewDay(final Campaign campaign, final LocalDate today, final protected boolean randomlyProcreates(final LocalDate today, final Person person) { if (canProcreate(today, person, true) != null) { return false; - } else if (person.getGenealogy().hasSpouse()) { - return relationshipProcreation(person); - } else if (isUseRelationshiplessProcreation()) { - return relationshiplessProcreation(person); } else { - return false; + return procreation(person); } } @@ -471,14 +583,5 @@ protected boolean randomlyProcreates(final LocalDate today, final Person person) * @param person the person to determine for * @return true if they do, otherwise false */ - protected abstract boolean relationshipProcreation(final Person person); - - /** - * Determines if a person without a partner procreates - * @param person the person to determine for - * @return true if they do, otherwise false - */ - protected abstract boolean relationshiplessProcreation(final Person person); - //endregion Random Procreation - //endregion New Day + protected abstract boolean procreation(final Person person); } diff --git a/MekHQ/src/mekhq/campaign/personnel/procreation/DisabledRandomProcreation.java b/MekHQ/src/mekhq/campaign/personnel/procreation/DisabledRandomProcreation.java index 2f729cc609..0c3cb78e9e 100644 --- a/MekHQ/src/mekhq/campaign/personnel/procreation/DisabledRandomProcreation.java +++ b/MekHQ/src/mekhq/campaign/personnel/procreation/DisabledRandomProcreation.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 - The MegaMek Team. All Rights Reserved. + * Copyright (C) 2021-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -37,12 +37,7 @@ protected boolean randomlyProcreates(final LocalDate today, final Person person) } @Override - protected boolean relationshipProcreation(final Person person) { - return false; - } - - @Override - protected boolean relationshiplessProcreation(final Person person) { + protected boolean procreation(final Person person) { return false; } } diff --git a/MekHQ/src/mekhq/campaign/personnel/procreation/PercentageRandomProcreation.java b/MekHQ/src/mekhq/campaign/personnel/procreation/PercentageRandomProcreation.java deleted file mode 100644 index 513729917c..0000000000 --- a/MekHQ/src/mekhq/campaign/personnel/procreation/PercentageRandomProcreation.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2021 - 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.procreation; - -import megamek.common.Compute; -import mekhq.campaign.CampaignOptions; -import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.enums.RandomProcreationMethod; - -public class PercentageRandomProcreation extends AbstractProcreation { - //region Variable Declarations - private double percentage; - private double relationshiplessPercentage; - //endregion Variable Declarations - - //region Constructors - public PercentageRandomProcreation(final CampaignOptions options) { - super(RandomProcreationMethod.PERCENTAGE, options); - setPercentage(options.getPercentageRandomProcreationRelationshipChance()); - setRelationshiplessPercentage(options.getPercentageRandomProcreationRelationshiplessChance()); - } - //endregion Constructors - - //region Getters/Setters - public double getPercentage() { - return percentage; - } - - public void setPercentage(final double percentage) { - this.percentage = percentage; - } - - public double getRelationshiplessPercentage() { - return relationshiplessPercentage; - } - - public void setRelationshiplessPercentage(final double relationshiplessPercentage) { - this.relationshiplessPercentage = relationshiplessPercentage; - } - //endregion Getters/Setters - - @Override - protected boolean relationshipProcreation(final Person person) { - return Compute.randomFloat() < getPercentage(); - } - - @Override - protected boolean relationshiplessProcreation(final Person person) { - return Compute.randomFloat() < getRelationshiplessPercentage(); - } -} diff --git a/MekHQ/src/mekhq/campaign/personnel/procreation/RandomProcreation.java b/MekHQ/src/mekhq/campaign/personnel/procreation/RandomProcreation.java new file mode 100644 index 0000000000..8a30e6ecc2 --- /dev/null +++ b/MekHQ/src/mekhq/campaign/personnel/procreation/RandomProcreation.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2021-2024 - 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.procreation; + +import megamek.common.Compute; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.enums.RandomProcreationMethod; + +/** + * Represents a random procreation method that is based on dice rolls. + */ +public class RandomProcreation extends AbstractProcreation { + //region Variable Declarations + private int relationshipDieSize; + private int relationshiplessDieSize; + //endregion Variable Declarations + + //region Constructors + /** + * Constructor to create a {@link RandomProcreation} object. This object is used to manage + * randomly determined procreation events within the game's campaign. + * + * @param options the campaign settings. + */ + public RandomProcreation(final CampaignOptions options) { + super(RandomProcreationMethod.DICE_ROLL, options); + setRelationshipDieSize(options.getRandomProcreationRelationshipDiceSize()); + setRelationshiplessDieSize(options.getRandomProcreationRelationshiplessDiceSize()); + } + //endregion Constructors + + //region Getters/Setters + /** + * Sets the size of the relationship die. + * The relationship die size determines the probability of procreation for a person who has a spouse. + * + * @param relationshipDieSize the size of the relationship die + */ + public void setRelationshipDieSize(final int relationshipDieSize) { + this.relationshipDieSize = relationshipDieSize; + } + + /** + * Sets the size of the relationshipless die. + * The relationship die size determines the probability of procreation for a person who does not + * have a spouse. + * + * @param relationshiplessDieSize the size of the relationship die + */ + public void setRelationshiplessDieSize(final int relationshiplessDieSize) { + this.relationshiplessDieSize = relationshiplessDieSize; + } + //endregion Getters/Setters + + @Override + protected boolean procreation(final Person person) { + int diceSize = person.getGenealogy().hasSpouse() ? relationshipDieSize : relationshiplessDieSize; + + if (diceSize == 0) { + return false; + } else if (diceSize == 1) { + return true; + } + + return Compute.randomInt(diceSize) == 0; + } +} diff --git a/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java b/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java index a3693dfe5a..2ea402711d 100644 --- a/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java +++ b/MekHQ/src/mekhq/campaign/universe/generators/companyGenerators/AbstractCompanyGenerator.java @@ -18,26 +18,8 @@ */ package mekhq.campaign.universe.generators.companyGenerators; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.ResourceBundle; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - import megamek.client.generator.RandomCallsignGenerator; -import megamek.common.AmmoType; -import megamek.common.Entity; -import megamek.common.EntityWeightClass; -import megamek.common.MekFileParser; -import megamek.common.MekSummary; -import megamek.common.UnitType; +import megamek.common.*; import megamek.common.annotations.Nullable; import megamek.common.options.OptionsConstants; import megamek.logging.MMLogger; @@ -63,6 +45,7 @@ import mekhq.campaign.personnel.Skill; import mekhq.campaign.personnel.SkillType; import mekhq.campaign.personnel.enums.PersonnelRole; +import mekhq.campaign.personnel.enums.education.EducationLevel; import mekhq.campaign.personnel.generator.AbstractPersonnelGenerator; import mekhq.campaign.personnel.ranks.Rank; import mekhq.campaign.unit.Unit; @@ -83,6 +66,14 @@ import mekhq.campaign.universe.selectors.planetSelectors.RangedPlanetSelector; import mekhq.campaign.work.WorkTime; +import java.time.LocalDate; +import java.util.*; +import java.util.Map.Entry; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + /** * Startup: * Second Panel: Presets, Date, Starting Faction, Starting Planet, AtB @@ -643,18 +634,26 @@ private void finalizePersonnel(final Campaign campaign, // Now that they are recruited, we can simulate backwards a few years and // generate marriages // and children + for (final CompanyGenerationPersonTracker tracker : trackers) { + if (tracker.getPerson().getExperienceLevel(campaign, false) > 0) { + tracker.getPerson().setEduHighestEducation(EducationLevel.COLLEGE); + } else { + tracker.getPerson().setEduHighestEducation(EducationLevel.HIGH_SCHOOL); + } + } + if (getOptions().isRunStartingSimulation()) { - LocalDate date = campaign.getLocalDate().minusYears(getOptions().getSimulationDuration()).minusDays(1); + LocalDate date = campaign.getLocalDate().minusYears(getOptions().getSimulationDuration()).minusWeeks(1); while (date.isBefore(campaign.getLocalDate())) { - date = date.plusDays(1); + date = date.plusWeeks(1); for (final CompanyGenerationPersonTracker tracker : trackers) { if (getOptions().isSimulateRandomMarriages()) { - campaign.getMarriage().processNewDay(campaign, date, tracker.getPerson()); + campaign.getMarriage().processNewWeek(campaign, date, tracker.getPerson()); } if (getOptions().isSimulateRandomProcreation()) { - campaign.getProcreation().processNewDay(campaign, date, tracker.getPerson()); + campaign.getProcreation().processNewWeek(campaign, date, tracker.getPerson()); } } } diff --git a/MekHQ/src/mekhq/gui/CampaignGUI.java b/MekHQ/src/mekhq/gui/CampaignGUI.java index a7d9f3e0c6..b7264912a9 100644 --- a/MekHQ/src/mekhq/gui/CampaignGUI.java +++ b/MekHQ/src/mekhq/gui/CampaignGUI.java @@ -54,11 +54,11 @@ import mekhq.campaign.personnel.death.AgeRangeRandomDeath; import mekhq.campaign.personnel.death.ExponentialRandomDeath; import mekhq.campaign.personnel.death.PercentageRandomDeath; -import mekhq.campaign.personnel.divorce.PercentageRandomDivorce; +import mekhq.campaign.personnel.divorce.RandomDivorce; import mekhq.campaign.personnel.enums.*; -import mekhq.campaign.personnel.marriage.PercentageRandomMarriage; +import mekhq.campaign.personnel.marriage.RandomMarriage; import mekhq.campaign.personnel.procreation.AbstractProcreation; -import mekhq.campaign.personnel.procreation.PercentageRandomProcreation; +import mekhq.campaign.personnel.procreation.RandomProcreation; import mekhq.campaign.personnel.ranks.RankSystem; import mekhq.campaign.personnel.ranks.Ranks; import mekhq.campaign.report.CargoReport; @@ -1492,11 +1492,9 @@ private void menuOptionsActionPerformed(final ActionEvent evt) { getCampaign().getDivorce().setUseRandomSameSexDivorce(newOptions.isUseRandomSameSexDivorce()); getCampaign().getDivorce().setUseRandomClanPersonnelDivorce(newOptions.isUseRandomClanPersonnelDivorce()); getCampaign().getDivorce().setUseRandomPrisonerDivorce(newOptions.isUseRandomPrisonerDivorce()); - if (getCampaign().getDivorce().getMethod().isPercentage()) { - ((PercentageRandomDivorce) getCampaign().getDivorce()).setOppositeSexPercentage( - newOptions.getPercentageRandomDivorceOppositeSexChance()); - ((PercentageRandomDivorce) getCampaign().getDivorce()).setSameSexPercentage( - newOptions.getPercentageRandomDivorceSameSexChance()); + if (getCampaign().getDivorce().getMethod().isDiceRoll()) { + ((RandomDivorce) getCampaign().getDivorce()).setDivorceDiceSize( + newOptions.getRandomDivorceDiceSize()); } } @@ -1505,15 +1503,12 @@ private void menuOptionsActionPerformed(final ActionEvent evt) { } else { getCampaign().getMarriage().setUseClanPersonnelMarriages(newOptions.isUseClanPersonnelMarriages()); getCampaign().getMarriage().setUsePrisonerMarriages(newOptions.isUsePrisonerMarriages()); - getCampaign().getMarriage().setUseRandomSameSexMarriages(newOptions.isUseRandomSameSexMarriages()); getCampaign().getMarriage() .setUseRandomClanPersonnelMarriages(newOptions.isUseRandomClanPersonnelMarriages()); getCampaign().getMarriage().setUseRandomPrisonerMarriages(newOptions.isUseRandomPrisonerMarriages()); - if (getCampaign().getMarriage().getMethod().isPercentage()) { - ((PercentageRandomMarriage) getCampaign().getMarriage()).setOppositeSexPercentage( - newOptions.getPercentageRandomMarriageOppositeSexChance()); - ((PercentageRandomMarriage) getCampaign().getMarriage()).setSameSexPercentage( - newOptions.getPercentageRandomMarriageSameSexChance()); + if (getCampaign().getMarriage().getMethod().isDiceRoll()) { + ((RandomMarriage) getCampaign().getMarriage()).setMarriageDiceSize( + newOptions.getRandomMarriageDiceSize()); } } @@ -1527,11 +1522,11 @@ private void menuOptionsActionPerformed(final ActionEvent evt) { getCampaign().getProcreation() .setUseRandomClanPersonnelProcreation(newOptions.isUseRandomClanPersonnelProcreation()); getCampaign().getProcreation().setUseRandomPrisonerProcreation(newOptions.isUseRandomPrisonerProcreation()); - if (getCampaign().getProcreation().getMethod().isPercentage()) { - ((PercentageRandomProcreation) getCampaign().getProcreation()).setPercentage( - newOptions.getPercentageRandomProcreationRelationshipChance()); - ((PercentageRandomProcreation) getCampaign().getProcreation()).setRelationshiplessPercentage( - newOptions.getPercentageRandomProcreationRelationshiplessChance()); + if (getCampaign().getProcreation().getMethod().isDiceRoll()) { + ((RandomProcreation) getCampaign().getProcreation()).setRelationshipDieSize( + newOptions.getRandomProcreationRelationshipDiceSize()); + ((RandomProcreation) getCampaign().getProcreation()).setRelationshiplessDieSize( + newOptions.getRandomProcreationRelationshiplessDiceSize()); } } diff --git a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java index fbcee127ca..5b89015eb6 100644 --- a/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java +++ b/MekHQ/src/mekhq/gui/adapter/PersonnelTableMouseAdapter.java @@ -18,27 +18,6 @@ */ package mekhq.gui.adapter; -import static megamek.client.ui.WrapLayout.wordWrap; -import static mekhq.campaign.personnel.education.Academy.skillParser; -import static mekhq.campaign.personnel.education.EducationController.getAcademy; -import static mekhq.campaign.personnel.education.EducationController.makeEnrollmentCheck; - -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.MouseEvent; -import java.time.LocalDate; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPopupMenu; -import javax.swing.JSplitPane; -import javax.swing.JTable; - import megamek.client.generator.RandomCallsignGenerator; import megamek.client.generator.RandomNameGenerator; import megamek.client.ui.dialogs.PortraitChooserDialog; @@ -62,26 +41,12 @@ import mekhq.campaign.finances.enums.TransactionType; import mekhq.campaign.log.LogEntry; import mekhq.campaign.log.PersonalLogger; -import mekhq.campaign.personnel.Award; -import mekhq.campaign.personnel.AwardsFactory; -import mekhq.campaign.personnel.Injury; -import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.PersonnelOptions; -import mekhq.campaign.personnel.SkillType; -import mekhq.campaign.personnel.SpecialAbility; +import mekhq.campaign.personnel.*; import mekhq.campaign.personnel.autoAwards.AutoAwardsController; import mekhq.campaign.personnel.education.Academy; import mekhq.campaign.personnel.education.AcademyFactory; import mekhq.campaign.personnel.education.EducationController; -import mekhq.campaign.personnel.enums.ManeiDominiClass; -import mekhq.campaign.personnel.enums.ManeiDominiRank; -import mekhq.campaign.personnel.enums.MergingSurnameStyle; -import mekhq.campaign.personnel.enums.PersonnelRole; -import mekhq.campaign.personnel.enums.PersonnelStatus; -import mekhq.campaign.personnel.enums.PrisonerStatus; -import mekhq.campaign.personnel.enums.Profession; -import mekhq.campaign.personnel.enums.ROMDesignation; -import mekhq.campaign.personnel.enums.SplittingSurnameStyle; +import mekhq.campaign.personnel.enums.*; import mekhq.campaign.personnel.enums.education.EducationLevel; import mekhq.campaign.personnel.enums.education.EducationStage; import mekhq.campaign.personnel.generator.SingleSpecialAbilityGenerator; @@ -104,6 +69,21 @@ import mekhq.gui.utilities.MultiLineTooltip; import mekhq.gui.utilities.StaticChecks; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.MouseEvent; +import java.time.LocalDate; +import java.util.List; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static megamek.client.ui.WrapLayout.wordWrap; +import static mekhq.campaign.personnel.education.Academy.skillParser; +import static mekhq.campaign.personnel.education.EducationController.getAcademy; +import static mekhq.campaign.personnel.education.EducationController.makeEnrollmentCheck; + public class PersonnelTableMouseAdapter extends JPopupMenuAdapter { private static final MMLogger logger = MMLogger.create(PersonnelTableMouseAdapter.class); @@ -171,6 +151,7 @@ public class PersonnelTableMouseAdapter extends JPopupMenuAdapter { private static final String CMD_JETTISON = "JETTISON"; private static final String CMD_RECRUIT = "RECRUIT"; private static final String CMD_ABTAKHA = "ABTAKHA"; + private static final String CMD_ADOPTION = "ADOPTION"; private static final String CMD_RANSOM = "RANSOM"; private static final String CMD_RANSOM_FRIENDLY = "RANSOM_FRIENDLY"; @@ -361,7 +342,7 @@ public void actionPerformed(ActionEvent action) { gui.getCampaign().getLocalDate(), person, false) == null)) .forEach(person -> { gui.getCampaign().getProcreation().addPregnancy( - gui.getCampaign(), gui.getCampaign().getLocalDate(), person); + gui.getCampaign(), gui.getCampaign().getLocalDate(), person, false); MekHQ.triggerEvent(new PersonChangedEvent(person)); }); break; @@ -379,12 +360,13 @@ public void actionPerformed(ActionEvent action) { .forEach(person -> gui.getCampaign().getDivorce().divorce(gui.getCampaign(), gui.getCampaign().getLocalDate(), person, SplittingSurnameStyle.valueOf(data[1]))); + break; } case CMD_ADD_SPOUSE: { gui.getCampaign().getMarriage().marry(gui.getCampaign(), gui.getCampaign().getLocalDate(), selectedPerson, gui.getCampaign().getPerson(UUID.fromString(data[1])), - MergingSurnameStyle.valueOf(data[2])); + MergingSurnameStyle.valueOf(data[2]), false); break; } case CMD_ADD_AWARD: { @@ -717,6 +699,34 @@ public void actionPerformed(ActionEvent action) { } break; } + case CMD_ADOPTION: { + Person orphan = gui.getCampaign().getPerson(UUID.fromString(data[1])); + + // clear the old parents + for (Person parent : orphan.getGenealogy().getParents()) { + orphan.getGenealogy().removeFamilyMember(FamilialRelationshipType.PARENT, parent); + } + + // add the new + for (Person person : people) { + person.getGenealogy().addFamilyMember(FamilialRelationshipType.CHILD, orphan); + orphan.getGenealogy().addFamilyMember(FamilialRelationshipType.PARENT, person); + + MekHQ.triggerEvent(new PersonChangedEvent(person)); + + if (person.getGenealogy().hasSpouse()) { + Person spouse = person.getGenealogy().getSpouse(); + + spouse.getGenealogy().addFamilyMember(FamilialRelationshipType.CHILD, orphan); + orphan.getGenealogy().addFamilyMember(FamilialRelationshipType.PARENT, spouse); + + MekHQ.triggerEvent(new PersonChangedEvent(spouse)); + } + } + + MekHQ.triggerEvent(new PersonChangedEvent(orphan)); + break; + } case CMD_RANSOM: { // ask the user if they want to sell off their prisoners. If yes, then add a // daily report entry, add the money and remove them all. @@ -1477,10 +1487,35 @@ protected Optional createPopupMenu() { popup.add(newMenuItem(resources.getString("abtakha.text"), CMD_ABTAKHA)); } + if ((oneSelected) && (!person.isChild(gui.getCampaign().getLocalDate()))) { + List orphans = gui.getCampaign().getActivePersonnel().stream() + .filter(child -> (child.isChild(gui.getCampaign().getLocalDate())) && (!child.getGenealogy().hasLivingParents())) + .toList(); + + if (!orphans.isEmpty()) { + JMenu orphanMenu = new JMenu(resources.getString("adopt.text")); + + for (final Person orphan : orphans) { + String status = String.format(resources.getString("adopt.description"), + orphan.getFullName(), + orphan.getGender(), + orphan.getAge(gui.getCampaign().getLocalDate())); + + JMenuItem orphanItem = new JMenuItem(status); + orphanItem.setActionCommand(makeCommand(CMD_ADOPTION, String.valueOf(orphan.getId()))); + orphanItem.addActionListener(this); + orphanMenu.add(orphanItem); + } + + JMenuHelpers.addMenuIfNonEmpty(popup, orphanMenu); + } + } + final PersonnelRole[] roles = PersonnelRole.values(); menu = new JMenu(resources.getString("changePrimaryRole.text")); + for (final PersonnelRole role : roles) { - if (person.canPerformRole(role, true)) { + if (person.canPerformRole(gui.getCampaign().getLocalDate(), person, role, true)) { cbMenuItem = new JCheckBoxMenuItem(role.getName(person.isClanPersonnel())); cbMenuItem.setActionCommand(makeCommand(CMD_PRIMARY_ROLE, role.name())); cbMenuItem.setSelected(person.getPrimaryRole() == role); @@ -1492,7 +1527,7 @@ protected Optional createPopupMenu() { menu = new JMenu(resources.getString("changeSecondaryRole.text")); for (final PersonnelRole role : roles) { - if (person.canPerformRole(role, false)) { + if (person.canPerformRole(gui.getCampaign().getLocalDate(), person, role, false)) { cbMenuItem = new JCheckBoxMenuItem(role.getName(person.isClanPersonnel())); cbMenuItem.setActionCommand(makeCommand(CMD_SECONDARY_ROLE, role.name())); cbMenuItem.setSelected(person.getSecondaryRole() == role); @@ -1522,8 +1557,7 @@ protected Optional createPopupMenu() { if (oneSelected && person.getStatus().isActive()) { if (gui.getCampaign().getCampaignOptions().isUseManualMarriages() - && (gui.getCampaign().getMarriage().canMarry(gui.getCampaign(), - gui.getCampaign().getLocalDate(), person, false) == null)) { + && (gui.getCampaign().getMarriage().canMarry(gui.getCampaign().getLocalDate(), person, false) == null)) { menu = new JMenu(resources.getString("chooseSpouse.text")); JMenu maleMenu = new JMenu(resources.getString("spouseMenuMale.text")); JMenu femaleMenu = new JMenu(resources.getString("spouseMenuFemale.text")); @@ -1545,16 +1579,22 @@ protected Optional createPopupMenu() { 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); + 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); + potentialSpouse.getFullName(), + potentialSpouse.getAge(today), + potentialSpouse.getRoleDesc(), + founder); } else { status = String.format(resources.getString("marriagePartnerDesc.format"), - potentialSpouse.getFullName(), potentialSpouse.getAge(today), - potentialSpouse.getRoleDesc(), founder); + potentialSpouse.getFullName(), + potentialSpouse.getAge(today), + potentialSpouse.getRoleDesc(), + founder); } spouseMenu = new JMenu(status); @@ -1583,8 +1623,10 @@ 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()) { @@ -2732,9 +2774,7 @@ protected Optional createPopupMenu() { || !gui.getCampaign().getCampaignOptions().getRandomMarriageMethod().isNone()) && Stream.of(selected).allMatch(p -> p.isMarriageable() == person.isMarriageable())) { cbMenuItem = new JCheckBoxMenuItem(resources.getString("miMarriageable.text")); - cbMenuItem.setToolTipText( - MultiLineTooltip.splitToolTip(String.format(resources.getString("miMarriageable.toolTipText"), - gui.getCampaign().getCampaignOptions().getMinimumMarriageAge()), 100)); + cbMenuItem.setToolTipText(wordWrap(resources.getString("miMarriageable.toolTipText"))); cbMenuItem.setName("miMarriageable"); cbMenuItem.setSelected(person.isMarriageable()); cbMenuItem.addActionListener(evt -> { diff --git a/MekHQ/src/mekhq/gui/dialog/CreateCharacterDialog.java b/MekHQ/src/mekhq/gui/dialog/CreateCharacterDialog.java index aaaac74387..44a9af261b 100644 --- a/MekHQ/src/mekhq/gui/dialog/CreateCharacterDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/CreateCharacterDialog.java @@ -69,26 +69,26 @@ public enum NameRestrictions { NONE } - private Person person; - private boolean editOrigin; - private boolean editBirthday; - private boolean editGender; + private final Person person; + private final boolean editOrigin; + private final boolean editBirthday; + private final boolean editGender; private boolean limitFaction; - private NameRestrictions nameRestrictions; + private final NameRestrictions nameRestrictions; private String instructions; - private int xpPool; + private final int xpPool; private Portrait portrait; private List optionComps = new ArrayList<>(); - private Map skillLvls = new Hashtable<>(); - private Map skillBonus = new Hashtable<>(); - private Map skillValues = new Hashtable<>(); - private Map skillChks = new Hashtable<>(); + private final Map skillLvls = new Hashtable<>(); + private final Map skillBonus = new Hashtable<>(); + private final Map skillValues = new Hashtable<>(); + private final Map skillChks = new Hashtable<>(); private PersonnelOptions options; private LocalDate birthdate; - private JFrame frame; + private final JFrame frame; private JButton btnDate; private JComboBox choiceGender; @@ -125,7 +125,7 @@ public enum NameRestrictions { private JButton doneButton; - private Campaign campaign; + private final Campaign campaign; private static final ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.CreateCharacterDialog", MekHQ.getMHQOptions().getLocale()); @@ -345,14 +345,9 @@ private JPanel getDemogPanel() { gridBagConstraints.insets = new Insets(0, 5, 0, 0); demogPanel.add(lblGender, gridBagConstraints); - DefaultComboBoxModel genderModel = new DefaultComboBoxModel<>(); - for (Gender gender : Gender.getExternalOptions()) { - genderModel.addElement(gender); - } - choiceGender = new JComboBox<>(genderModel); + choiceGender = new JComboBox<>(Gender.values()); choiceGender.setName("choiceGender"); - choiceGender.setSelectedItem(person.getGender().isExternal() ? person.getGender() - : person.getGender().getExternalVariant()); + choiceGender.setSelectedItem(person.getGender()); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = y; @@ -1454,9 +1449,7 @@ private void done() { ? "" : textBloodname.getText()); person.setBiography(txtBio.getText()); if (choiceGender.getSelectedItem() != null) { - person.setGender(person.getGender().isInternal() - ? ((Gender) choiceGender.getSelectedItem()).getInternalVariant() - : (Gender) choiceGender.getSelectedItem()); + person.setGender((Gender) choiceGender.getSelectedItem()); } person.setBirthday(birthdate); person.setOriginFaction((Faction) choiceFaction.getSelectedItem()); diff --git a/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java b/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java index 4c5b25dbe2..80ecefb149 100644 --- a/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/CustomizePersonDialog.java @@ -84,7 +84,7 @@ /** * This dialog is used to both hire new pilots and to edit existing ones - * + * * @author Jay Lawson (jaylawson39 at yahoo.com) */ public class CustomizePersonDialog extends JDialog implements DialogOptionListener { @@ -93,16 +93,16 @@ public class CustomizePersonDialog extends JDialog implements DialogOptionListen // region Variable declarations private Person person; private List optionComps = new ArrayList<>(); - private Map skillLvls = new Hashtable<>(); - private Map skillBonus = new Hashtable<>(); - private Map skillValues = new Hashtable<>(); - private Map skillChks = new Hashtable<>(); + private final Map skillLvls = new Hashtable<>(); + private final Map skillBonus = new Hashtable<>(); + private final Map skillValues = new Hashtable<>(); + private final Map skillChks = new Hashtable<>(); private PersonnelOptions options; private LocalDate birthdate; private LocalDate recruitment; private LocalDate lastRankChangeDate; private LocalDate retirement; - private JFrame frame; + private final JFrame frame; private JButton btnDate; private JButton btnServiceDate; @@ -146,7 +146,7 @@ public class CustomizePersonDialog extends JDialog implements DialogOptionListen private MMComboBox comboPersonalityQuirk; private MMComboBox comboIntelligence; - private Campaign campaign; + private final Campaign campaign; private final transient ResourceBundle resourceMap = ResourceBundle.getBundle( "mekhq.resources.CustomizePersonDialog", @@ -355,14 +355,9 @@ private void initComponents() { gridBagConstraints.insets = new Insets(0, 5, 0, 0); panDemog.add(lblGender, gridBagConstraints); - DefaultComboBoxModel genderModel = new DefaultComboBoxModel<>(); - for (Gender gender : Gender.getExternalOptions()) { - genderModel.addElement(gender); - } - choiceGender = new JComboBox<>(genderModel); + choiceGender = new JComboBox<>(Gender.values()); choiceGender.setName("choiceGender"); - choiceGender.setSelectedItem(person.getGender().isExternal() ? person.getGender() - : person.getGender().getExternalVariant()); + choiceGender.setSelectedItem(person.getGender()); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = y; @@ -1196,9 +1191,7 @@ private void btnOkActionPerformed(ActionEvent evt) { person.setBiography(txtBio.getText()); if (choiceGender.getSelectedItem() != null) { - person.setGender(person.getGender().isInternal() - ? ((Gender) choiceGender.getSelectedItem()).getInternalVariant() - : (Gender) choiceGender.getSelectedItem()); + person.setGender((Gender) choiceGender.getSelectedItem()); } person.setBirthday(birthdate); diff --git a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java index ab6d22f00e..083d5309d1 100644 --- a/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/GMToolsDialog.java @@ -1,7 +1,7 @@ /* * GMToolsDialog.java * - * Copyright (c) 2013-2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2013-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -1385,16 +1385,14 @@ private void setValuesFromPerson() { getLblCurrentName().setText(getPerson().getFullName()); // Gender is set based on the person's gender - getComboGender().setSelectedItem(getPerson().getGender().isExternal() ? getPerson().getGender() - : getPerson().getGender().getExternalVariant()); + getComboGender().setSelectedItem(getPerson().getGender()); // Current Callsign is set if applicable if (!StringUtility.isNullOrBlank(getPerson().getCallsign())) { getLblCurrentCallsign().setText(getPerson().getCallsign()); } - // We set the clan personnel value based on whether or not the person is clan - // personell + // We set the clan personnel value based on whether the person is clan personnel getChkClanPersonnel().setSelected(getPerson().isClanPersonnel()); // Now we figure out the person's origin faction diff --git a/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java b/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java index cff27f2aee..f612f60ac6 100644 --- a/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java +++ b/MekHQ/src/mekhq/gui/dialog/HireBulkPersonnelDialog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2010-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -40,6 +40,7 @@ import megamek.client.ui.preferences.PreferencesNode; import megamek.common.Compute; import megamek.common.enums.SkillLevel; +import megamek.common.options.IOption; import megamek.logging.MMLogger; import mekhq.MekHQ; import mekhq.Utilities; @@ -47,11 +48,31 @@ import mekhq.campaign.RandomSkillPreferences; import mekhq.campaign.personnel.Person; import mekhq.campaign.personnel.SkillType; +import mekhq.campaign.personnel.PersonnelOptions; import mekhq.campaign.personnel.education.EducationController; import mekhq.campaign.personnel.enums.PersonnelRole; import mekhq.campaign.personnel.enums.Profession; +import mekhq.campaign.personnel.generator.AbstractSpecialAbilityGenerator; +import mekhq.campaign.personnel.generator.DefaultSpecialAbilityGenerator; import mekhq.gui.CampaignGUI; import mekhq.gui.displayWrappers.RankDisplay; +import org.apache.logging.log4j.LogManager; + +import javax.swing.*; +import javax.swing.JSpinner.DefaultEditor; +import javax.swing.JSpinner.NumberEditor; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Objects; +import java.util.ResourceBundle; + +import static mekhq.campaign.personnel.SkillType.*; +import static mekhq.campaign.personnel.generator.AbstractSkillGenerator.addSkill; /** * @author Jay Lawson @@ -62,7 +83,7 @@ public class HireBulkPersonnelDialog extends JDialog { private static final Insets ZERO_INSETS = new Insets(0, 0, 0, 0); private static final Insets DEFAULT_INSETS = new Insets(5, 5, 5, 5); - private Campaign campaign; + private final Campaign campaign; private JComboBox choiceType; private JComboBox choiceRanks; @@ -356,19 +377,38 @@ private void hire(boolean isGmHire) { person.setBirthday(birthDay); age = person.getAge(today); } - } - // Limit skills by age for children and adolescents - if (age < 12) { - person.removeAllSkills(); - } else if (age < 14) { - person.limitSkills(0); - } else if (age < 18) { - person.limitSkills(age - 13); + // Limit skills by age for children and adolescents + if (age < 16) { + person.removeAllSkills(); + } else if (age < 18) { + person.limitSkills(0); + } + + // re-roll SPAs to include in any age and skill adjustments + Enumeration options = new PersonnelOptions().getOptions(PersonnelOptions.LVL3_ADVANTAGES); + + for (IOption option : Collections.list(options)) { + person.getOptions().getOption(option.getName()).clearValue(); + } + + int experienceLevel = person.getExperienceLevel(campaign, false); + + if (experienceLevel <= 0) { + person.setLoyalty(Compute.d6(3) + 2); + } else if (experienceLevel == 1) { + person.setLoyalty(Compute.d6(3) + 1); + } else { + person.setLoyalty(Compute.d6(3)); + } + + if (experienceLevel > 0) { + AbstractSpecialAbilityGenerator specialAbilityGenerator = new DefaultSpecialAbilityGenerator(); + specialAbilityGenerator.setSkillPreferences(new RandomSkillPreferences()); + specialAbilityGenerator.generateSpecialAbilities(campaign, person, experienceLevel); + } } - // re-calculate initial education, as this might have changed since the person was first generated - EducationController.setInitialEducation(campaign, person); if (!campaign.recruitPerson(person, isGmHire)) { number = 0; diff --git a/MekHQ/src/mekhq/gui/enums/PersonnelFilter.java b/MekHQ/src/mekhq/gui/enums/PersonnelFilter.java index d54387ad31..28f5d3b35d 100644 --- a/MekHQ/src/mekhq/gui/enums/PersonnelFilter.java +++ b/MekHQ/src/mekhq/gui/enums/PersonnelFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2020-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -18,6 +18,11 @@ */ package mekhq.gui.enums; +import mekhq.MekHQ; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.enums.PersonnelRole; +import mekhq.campaign.personnel.enums.PersonnelStatus; + import java.time.LocalDate; import java.util.Arrays; import java.util.List; @@ -25,10 +30,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import mekhq.MekHQ; -import mekhq.campaign.personnel.Person; -import mekhq.campaign.personnel.enums.PersonnelRole; - public enum PersonnelFilter { // region Enum Declarations // region Standard Personnel Filters @@ -377,188 +378,135 @@ public boolean getFilteredInformation(final Person person, LocalDate currentDate final boolean active = person.getStatus().isActive() && !person.getPrisonerStatus().isCurrentPrisoner(); final boolean dead = person.getStatus().isDead(); - switch (this) { - case ALL: - return true; - case ACTIVE: - return active; - case COMBAT: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isCombat() - : person.hasCombatRole()); - case SUPPORT: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? !person.getPrimaryRole().isCombat() - : person.hasSupportRole(true)); - case MEKWARRIORS: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isMekWarriorGrouping() - : (person.getPrimaryRole().isMekWarriorGrouping() - || person.getSecondaryRole().isMekWarriorGrouping())); - case MEKWARRIOR: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isMekWarrior() - : person.hasRole(PersonnelRole.MEKWARRIOR)); - case LAM_PILOT: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isLAMPilot() - : person.hasRole(PersonnelRole.LAM_PILOT)); - case VEHICLE_CREWMEMBER: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isVehicleCrewMember() - : (person.getPrimaryRole().isVehicleCrewMember() - || person.getSecondaryRole().isVehicleCrewMember())); - case GROUND_VEHICLE_DRIVER: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isGroundVehicleDriver() - : person.hasRole(PersonnelRole.GROUND_VEHICLE_DRIVER)); - case NAVAL_VEHICLE_DRIVER: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isNavalVehicleDriver() - : person.hasRole(PersonnelRole.NAVAL_VEHICLE_DRIVER)); - case VEHICLE_GUNNER: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isVehicleGunner() - : person.hasRole(PersonnelRole.VEHICLE_GUNNER)); - case VEHICLE_CREW: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isVehicleCrew() - : person.hasRole(PersonnelRole.VEHICLE_CREW)); - case VTOL_PILOT: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isVTOLPilot() - : person.hasRole(PersonnelRole.VTOL_PILOT)); - case AEROSPACE_PILOT: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isAerospacePilot() - : person.hasRole(PersonnelRole.AEROSPACE_PILOT)); - case CONVENTIONAL_AIRCRAFT_PILOT: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isConventionalAircraftPilot() - : person.hasRole(PersonnelRole.CONVENTIONAL_AIRCRAFT_PILOT)); - case PROTOMEK_PILOT: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isProtoMekPilot() - : person.hasRole(PersonnelRole.PROTOMEK_PILOT)); - case BATTLE_ARMOUR: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isBattleArmour() - : person.hasRole(PersonnelRole.BATTLE_ARMOUR)); - case SOLDIER: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isSoldier() - : person.hasRole(PersonnelRole.SOLDIER)); - case VESSEL_CREWMEMBER: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isVesselCrewMember() - : (person.getPrimaryRole().isVesselCrewMember() - || person.getSecondaryRole().isVesselCrewMember())); - case VESSEL_PILOT: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isVesselPilot() - : person.hasRole(PersonnelRole.VESSEL_PILOT)); - case VESSEL_CREW: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isVesselCrew() - : person.hasRole(PersonnelRole.VESSEL_CREW)); - case VESSEL_GUNNER: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isVesselGunner() - : person.hasRole(PersonnelRole.VESSEL_GUNNER)); - case VESSEL_NAVIGATOR: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isVesselNavigator() - : person.hasRole(PersonnelRole.VESSEL_NAVIGATOR)); - case TECH: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isTech() - : person.isTech()); - case MEK_TECH: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isMekTech() - : person.hasRole(PersonnelRole.MEK_TECH)); - case MECHANIC: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isMechanic() - : person.hasRole(PersonnelRole.MECHANIC)); - case AERO_TECH: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isAeroTek() - : person.hasRole(PersonnelRole.AERO_TEK)); - case BA_TECH: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isBATech() - : person.hasRole(PersonnelRole.BA_TECH)); - case ASTECH: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isAstech() - : person.hasRole(PersonnelRole.ASTECH)); - case MEDICAL: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isMedicalStaff() - : (person.getPrimaryRole().isMedicalStaff() || person.getSecondaryRole().isMedicalStaff())); - case DOCTOR: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isDoctor() - : person.hasRole(PersonnelRole.DOCTOR)); - case MEDIC: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isMedic() - : person.hasRole(PersonnelRole.MEDIC)); - case ADMINISTRATOR: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isAdministrator() - : person.isAdministrator()); - case ADMINISTRATOR_COMMAND: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isAdministratorCommand() - : person.hasRole(PersonnelRole.ADMINISTRATOR_COMMAND)); - case ADMINISTRATOR_LOGISTICS: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isAdministratorLogistics() - : person.hasRole(PersonnelRole.ADMINISTRATOR_LOGISTICS)); - case ADMINISTRATOR_TRANSPORT: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isAdministratorTransport() - : person.hasRole(PersonnelRole.ADMINISTRATOR_TRANSPORT)); - case ADMINISTRATOR_HR: - return active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() - ? person.getPrimaryRole().isAdministratorHR() - : person.hasRole(PersonnelRole.ADMINISTRATOR_HR)); - case DEPENDENT: - return ((!dead) && (active && person.getPrimaryRole().isDependent())); - case FOUNDER: - return ((!dead) && (person.isFounder())); - case KIDS: - return ((!dead) && (!person.getStatus().isLeft()) && (person.isChild(currentDate))); - case PRISONER: - return ((!dead) && ((person.getPrisonerStatus().isCurrentPrisoner()) - || (person.getPrisonerStatus().isBondsman()))); - case INACTIVE: - return ((!dead) && (!person.getStatus().isActive())); - case ON_LEAVE: - return person.getStatus().isOnLeave(); - case MIA: - return person.getStatus().isMIA() || person.getStatus().isPoW(); - 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: - return person.getStatus().isStudent(); - case MISSING: - return person.getStatus().isMissing(); - case KIA: - return person.getStatus().isKIA(); - case DEAD: - return person.getStatus().isDead(); - default: - return false; - } + PersonnelStatus status = person.getStatus(); + + return switch (this) { + case ALL -> true; + case ACTIVE -> active; + case COMBAT -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isCombat() : person.hasCombatRole()); + case SUPPORT -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + !person.getPrimaryRole().isCombat() : person.hasSupportRole(true)); + case MEKWARRIORS -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isMekWarriorGrouping() : + (person.getPrimaryRole().isMekWarriorGrouping() || person.getSecondaryRole().isMekWarriorGrouping())); + case MEKWARRIOR -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isMekWarrior() : person.hasRole(PersonnelRole.MEKWARRIOR)); + case LAM_PILOT -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isLAMPilot() : person.hasRole(PersonnelRole.LAM_PILOT)); + case VEHICLE_CREWMEMBER -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isVehicleCrew() : + (person.getPrimaryRole().isVehicleCrew() || person.getSecondaryRole().isVehicleCrew())); + case GROUND_VEHICLE_DRIVER -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isGroundVehicleDriver() : person.hasRole(PersonnelRole.GROUND_VEHICLE_DRIVER)); + case NAVAL_VEHICLE_DRIVER -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isNavalVehicleDriver() : person.hasRole(PersonnelRole.NAVAL_VEHICLE_DRIVER)); + case VEHICLE_GUNNER -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isVehicleGunner() : person.hasRole(PersonnelRole.VEHICLE_GUNNER)); + case VEHICLE_CREW -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isVehicleCrew() : person.hasRole(PersonnelRole.VEHICLE_CREW)); + case VTOL_PILOT -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isVTOLPilot() : person.hasRole(PersonnelRole.VTOL_PILOT)); + case AEROSPACE_PILOT -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isAerospacePilot() : person.hasRole(PersonnelRole.AEROSPACE_PILOT)); + case CONVENTIONAL_AIRCRAFT_PILOT -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isConventionalAircraftPilot() : person.hasRole(PersonnelRole.CONVENTIONAL_AIRCRAFT_PILOT)); + case PROTOMEK_PILOT -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isProtoMekPilot() : person.hasRole(PersonnelRole.PROTOMEK_PILOT)); + case BATTLE_ARMOUR -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isBattleArmour() : person.hasRole(PersonnelRole.BATTLE_ARMOUR)); + case SOLDIER -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isSoldier() : person.hasRole(PersonnelRole.SOLDIER)); + case VESSEL_CREWMEMBER -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isVesselCrew() : + (person.getPrimaryRole().isVesselCrew() || person.getSecondaryRole().isVesselCrew())); + case VESSEL_PILOT -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isVesselPilot() : person.hasRole(PersonnelRole.VESSEL_PILOT)); + case VESSEL_CREW -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isVesselCrew() : person.hasRole(PersonnelRole.VESSEL_CREW)); + case VESSEL_GUNNER -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isVesselGunner() : person.hasRole(PersonnelRole.VESSEL_GUNNER)); + case VESSEL_NAVIGATOR -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isVesselNavigator() : person.hasRole(PersonnelRole.VESSEL_NAVIGATOR)); + case TECH -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isTech() : person.isTech()); + case MEK_TECH -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isMekTech() : person.hasRole(PersonnelRole.MEK_TECH)); + case MECHANIC -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isMechanic() : person.hasRole(PersonnelRole.MECHANIC)); + case AERO_TECH -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isAeroTek() : person.hasRole(PersonnelRole.AERO_TEK)); + case BA_TECH -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isBATech() : person.hasRole(PersonnelRole.BA_TECH)); + case ASTECH -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isAstech() : person.hasRole(PersonnelRole.ASTECH)); + case MEDICAL -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isMedicalStaff() : (person.getPrimaryRole().isMedicalStaff() || person.getSecondaryRole().isMedicalStaff())); + case DOCTOR -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isDoctor() : person.hasRole(PersonnelRole.DOCTOR)); + case MEDIC -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isMedic() : person.hasRole(PersonnelRole.MEDIC)); + case ADMINISTRATOR -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isAdministrator() : person.isAdministrator()); + case ADMINISTRATOR_COMMAND -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isAdministratorCommand() : person.hasRole(PersonnelRole.ADMINISTRATOR_COMMAND)); + case ADMINISTRATOR_LOGISTICS -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isAdministratorLogistics() : person.hasRole(PersonnelRole.ADMINISTRATOR_LOGISTICS)); + case ADMINISTRATOR_TRANSPORT -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isAdministratorTransport() : person.hasRole(PersonnelRole.ADMINISTRATOR_TRANSPORT)); + case ADMINISTRATOR_HR -> + active && (MekHQ.getMHQOptions().getPersonnelFilterOnPrimaryRole() ? + person.getPrimaryRole().isAdministratorHR() : person.hasRole(PersonnelRole.ADMINISTRATOR_HR)); + case DEPENDENT -> ((!dead) && (active && person.getPrimaryRole().isDependent())); + case FOUNDER -> ((!dead) && (person.isFounder())); + case KIDS -> ((!dead) && (!status.isLeft()) && (person.isChild(currentDate))); + case PRISONER -> ((!dead) && ((person.getPrisonerStatus().isCurrentPrisoner()) || (person.getPrisonerStatus().isBondsman()))); + case INACTIVE -> ((!dead) && (!status.isActive())); + case ON_LEAVE -> status.isOnLeave() || status.isOnMaternityLeave(); + case MIA -> status.isMIA() || status.isPoW(); + case RETIRED -> status.isRetired(); + case RESIGNED -> ((status.isResigned()) || (status.isLeft())); + case AWOL -> status.isAwol(); + case DESERTED -> status.isDeserted(); + case STUDENT -> status.isStudent(); + case MISSING -> status.isMissing(); + case KIA -> status.isKIA(); + case DEAD -> status.isDead(); + }; } @Override diff --git a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java index f9877dff18..ed91c9ed75 100644 --- a/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java +++ b/MekHQ/src/mekhq/gui/panes/CampaignOptionsPane.java @@ -364,27 +364,30 @@ public class CampaignOptionsPane extends AbstractMHQTabbedPane { // region Life Paths Tab // Personnel Randomization private JCheckBox chkUseDylansRandomXP; + private JSpinner spnNonBinaryDiceSize; + + // Random Histories private RandomOriginOptionsPanel randomOriginOptionsPanel; private JCheckBox chkUseRandomPersonalities; private JCheckBox chkUseRandomPersonalityReputation; private JCheckBox chkUseIntelligenceXpMultiplier; + private JCheckBox chkUseSimulatedRelationships; // Marriage private JCheckBox chkUseManualMarriages; private JCheckBox chkUseClanPersonnelMarriages; private JCheckBox chkUsePrisonerMarriages; - private JSpinner spnMinimumMarriageAge; private JSpinner spnCheckMutualAncestorsDepth; + private JSpinner spnNoInterestInMarriageDiceSize; private JCheckBox chkLogMarriageNameChanges; private Map spnMarriageSurnameWeights; private MMComboBox comboRandomMarriageMethod; - private JCheckBox chkUseRandomSameSexMarriages; private JCheckBox chkUseRandomClanPersonnelMarriages; private JCheckBox chkUseRandomPrisonerMarriages; private JSpinner spnRandomMarriageAgeRange; - private JSpinner spnPercentageRandomMarriageOppositeSexChance; - private JLabel lblPercentageRandomMarriageSameSexChance; - private JSpinner spnPercentageRandomMarriageSameSexChance; + private JSpinner spnRandomMarriageDiceSize; + private JSpinner spnRandomSameSexMarriageDiceSize; + private JSpinner spnRandomNewDependentMarriage; // Divorce private JCheckBox chkUseManualDivorce; @@ -396,10 +399,8 @@ public class CampaignOptionsPane extends AbstractMHQTabbedPane { private JCheckBox chkUseRandomSameSexDivorce; private JCheckBox chkUseRandomClanPersonnelDivorce; private JCheckBox chkUseRandomPrisonerDivorce; - private JLabel lblPercentageRandomDivorceOppositeSexChance; - private JSpinner spnPercentageRandomDivorceOppositeSexChance; - private JLabel lblPercentageRandomDivorceSameSexChance; - private JSpinner spnPercentageRandomDivorceSameSexChance; + private JLabel lblRandomDivorceDiceSize; + private JSpinner spnRandomDivorceDiceSize; // Procreation private JCheckBox chkUseManualProcreation; @@ -411,14 +412,16 @@ public class CampaignOptionsPane extends AbstractMHQTabbedPane { private JCheckBox chkAssignChildrenOfFoundersFounderTag; private JCheckBox chkDetermineFatherAtBirth; private JCheckBox chkDisplayTrueDueDate; + private JSpinner spnNoInterestInChildrenDiceSize; + private JCheckBox chkUseMaternityLeave; private JCheckBox chkLogProcreation; private MMComboBox comboRandomProcreationMethod; private JCheckBox chkUseRelationshiplessRandomProcreation; private JCheckBox chkUseRandomClanPersonnelProcreation; private JCheckBox chkUseRandomPrisonerProcreation; - private JSpinner spnPercentageRandomProcreationRelationshipChance; - private JLabel lblPercentageRandomProcreationRelationshiplessChance; - private JSpinner spnPercentageRandomProcreationRelationshiplessChance; + private JSpinner spnRandomProcreationRelationshipDiceSize; + private JLabel lblRandomProcreationRelationshiplessDiceSize; + private JSpinner spnRandomProcreationRelationshiplessDiceSize; // Awards private MMComboBox comboAwardBonusStyle; @@ -3060,8 +3063,7 @@ private JScrollPane createAgainstTheBotTab() { gridBagConstraints.gridy = 4; panSubAtBContract.add(spnBonusPartExchangeValue, gridBagConstraints); - JLabel lblBonusPartMaxExchangeCount = new JLabel( - resources.getString("lblBonusPartMaxExchangeCount.text")); + JLabel lblBonusPartMaxExchangeCount = new JLabel(resources.getString("lblBonusPartMaxExchangeCount.text")); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 5; gridBagConstraints.gridwidth = 1; @@ -4617,8 +4619,7 @@ public Component getListCellRendererComponent(final JList list, final Object chkUseSubContractSoldiers.setEnabled(isUseTurnover); JLabel lblServiceContractDuration = new JLabel(resources.getString("lblServiceContractDuration.text")); - lblServiceContractDuration - .setToolTipText(resources.getString("lblServiceContractDuration.toolTipText")); + lblServiceContractDuration.setToolTipText(resources.getString("lblServiceContractDuration.toolTipText")); lblServiceContractDuration.setName("lblServiceContractDuration"); lblServiceContractDuration.setEnabled(isUseTurnover); @@ -4629,8 +4630,7 @@ public Component getListCellRendererComponent(final JList list, final Object spnServiceContractDuration.setEnabled(isUseTurnover); JLabel lblServiceContractModifier = new JLabel(resources.getString("lblServiceContractModifier.text")); - lblServiceContractModifier - .setToolTipText(resources.getString("lblServiceContractModifier.toolTipText")); + lblServiceContractModifier.setToolTipText(resources.getString("lblServiceContractModifier.toolTipText")); lblServiceContractModifier.setName("lblServiceContractModifier"); lblServiceContractModifier.setEnabled(isUseTurnover); @@ -4893,10 +4893,8 @@ private JPanel createTurnoverAndRetentionPayoutPanel() { spnPayoutRateEnlisted.setName("lblPayoutRateEnlisted"); spnPayoutRateEnlisted.setEnabled(isUseTurnover); - JLabel lblPayoutRetirementMultiplier = new JLabel( - resources.getString("lblPayoutRetirementMultiplier.text")); - lblPayoutRetirementMultiplier - .setToolTipText(resources.getString("lblPayoutRetirementMultiplier.toolTipText")); + JLabel lblPayoutRetirementMultiplier = new JLabel(resources.getString("lblPayoutRetirementMultiplier.text")); + lblPayoutRetirementMultiplier.setToolTipText(resources.getString("lblPayoutRetirementMultiplier.toolTipText")); lblPayoutRetirementMultiplier.setName("lblPayoutRetirementMultiplier"); lblPayoutRetirementMultiplier.setEnabled(isUseTurnover); @@ -5170,6 +5168,14 @@ private JPanel createPersonnelRandomizationPanel() { chkUseDylansRandomXP.setToolTipText(resources.getString("chkUseDylansRandomXP.toolTipText")); chkUseDylansRandomXP.setName("chkUseDylansRandomXP"); + JLabel lblNonBinaryDiceSize = new JLabel(resources.getString("lblNonBinaryDiceSize.text")); + lblNonBinaryDiceSize.setToolTipText(resources.getString("lblNonBinaryDiceSize.toolTipText")); + lblNonBinaryDiceSize.setName("lblNonBinaryDiceSize"); + + spnNonBinaryDiceSize = new JSpinner(new SpinnerNumberModel(60, 0, 100000, 1)); + spnNonBinaryDiceSize.setToolTipText(wordWrap(resources.getString("lblNonBinaryDiceSize.toolTipText"))); + spnNonBinaryDiceSize.setName("spnNonBinaryDiceSize"); + // Layout the Panel final JPanel panel = new JPanel(); panel.setBorder(BorderFactory @@ -5183,11 +5189,19 @@ private JPanel createPersonnelRandomizationPanel() { layout.setVerticalGroup( layout.createSequentialGroup() - .addComponent(chkUseDylansRandomXP)); + .addComponent(chkUseDylansRandomXP) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblNonBinaryDiceSize) + .addComponent(spnNonBinaryDiceSize, Alignment.LEADING)) + ); layout.setHorizontalGroup( layout.createParallelGroup(Alignment.LEADING) - .addComponent(chkUseDylansRandomXP)); + .addComponent(chkUseDylansRandomXP) + .addGroup(layout.createSequentialGroup() + .addComponent(lblNonBinaryDiceSize) + .addComponent(spnNonBinaryDiceSize)) + ); return panel; } @@ -5211,6 +5225,10 @@ private JPanel createRandomHistoriesPanel() { .setToolTipText(resources.getString("chkUseIntelligenceXpMultiplier.toolTipText")); chkUseIntelligenceXpMultiplier.setName("chkUseIntelligenceXpMultiplier"); + chkUseSimulatedRelationships = new JCheckBox(resources.getString("chkUseSimulatedRelationships.text")); + chkUseSimulatedRelationships.setToolTipText(resources.getString("chkUseSimulatedRelationships.toolTipText")); + chkUseSimulatedRelationships.setName("chkUseSimulatedRelationships"); + // Layout the Panel final JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createTitledBorder(resources.getString("randomHistoriesPanel.title"))); @@ -5226,14 +5244,18 @@ private JPanel createRandomHistoriesPanel() { .addComponent(randomOriginOptionsPanel) .addComponent(chkUseRandomPersonalities) .addComponent(chkUseRandomPersonalityReputation) - .addComponent(chkUseIntelligenceXpMultiplier)); + .addComponent(chkUseIntelligenceXpMultiplier) + .addComponent(chkUseSimulatedRelationships) + ); layout.setHorizontalGroup( layout.createParallelGroup(Alignment.LEADING) .addComponent(randomOriginOptionsPanel) .addComponent(chkUseRandomPersonalities) .addComponent(chkUseRandomPersonalityReputation) - .addComponent(chkUseIntelligenceXpMultiplier)); + .addComponent(chkUseIntelligenceXpMultiplier) + .addComponent(chkUseSimulatedRelationships) + ); return panel; } @@ -5739,13 +5761,13 @@ private JPanel createMarriagePanel() { .setEnabled(!method.isNone() && chkUsePrisonerMarriages.isSelected()); }); - final JLabel lblMinimumMarriageAge = new JLabel(resources.getString("lblMinimumMarriageAge.text")); - lblMinimumMarriageAge.setToolTipText(resources.getString("lblMinimumMarriageAge.toolTipText")); - lblMinimumMarriageAge.setName("lblMinimumMarriageAge"); + final JLabel lblNoInterestInMarriageDiceSize = new JLabel(resources.getString("lblNoInterestInMarriageDiceSize.text")); + lblNoInterestInMarriageDiceSize.setToolTipText(wordWrap(resources.getString("lblNoInterestInMarriageDiceSize.toolTipText"))); + lblNoInterestInMarriageDiceSize.setName("lblNoInterestInMarriageDiceSize"); - spnMinimumMarriageAge = new JSpinner(new SpinnerNumberModel(16, 14, null, 1)); - spnMinimumMarriageAge.setToolTipText(resources.getString("lblMinimumMarriageAge.toolTipText")); - spnMinimumMarriageAge.setName("spnMinimumMarriageAge"); + spnNoInterestInMarriageDiceSize = new JSpinner(new SpinnerNumberModel(10, 1, 100000, 1)); + spnNoInterestInMarriageDiceSize.setToolTipText(wordWrap(resources.getString("lblNoInterestInMarriageDiceSize.toolTipText"))); + spnNoInterestInMarriageDiceSize.setName("spnNoInterestInMarriageDiceSize"); final JLabel lblCheckMutualAncestorsDepth = new JLabel( resources.getString("lblCheckMutualAncestorsDepth.text")); @@ -5767,7 +5789,6 @@ private JPanel createMarriagePanel() { final JPanel randomMarriagePanel = createRandomMarriagePanel(); // Programmatically Assign Accessibility Labels - lblMinimumMarriageAge.setLabelFor(spnMinimumMarriageAge); lblCheckMutualAncestorsDepth.setLabelFor(spnCheckMutualAncestorsDepth); // Layout the Panel @@ -5786,8 +5807,8 @@ private JPanel createMarriagePanel() { .addComponent(chkUseClanPersonnelMarriages) .addComponent(chkUsePrisonerMarriages) .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblMinimumMarriageAge) - .addComponent(spnMinimumMarriageAge, Alignment.LEADING)) + .addComponent(lblNoInterestInMarriageDiceSize) + .addComponent(spnNoInterestInMarriageDiceSize, Alignment.LEADING)) .addGroup(layout.createParallelGroup(Alignment.BASELINE) .addComponent(lblCheckMutualAncestorsDepth) .addComponent(spnCheckMutualAncestorsDepth, @@ -5802,8 +5823,8 @@ private JPanel createMarriagePanel() { .addComponent(chkUseClanPersonnelMarriages) .addComponent(chkUsePrisonerMarriages) .addGroup(layout.createSequentialGroup() - .addComponent(lblMinimumMarriageAge) - .addComponent(spnMinimumMarriageAge)) + .addComponent(lblNoInterestInMarriageDiceSize) + .addComponent(spnNoInterestInMarriageDiceSize)) .addGroup(layout.createSequentialGroup() .addComponent(lblCheckMutualAncestorsDepth) .addComponent(spnCheckMutualAncestorsDepth)) @@ -5874,33 +5895,12 @@ public Component getListCellRendererComponent(final JList list, final Object return; } final boolean enabled = !method.isNone(); - final boolean sameSexEnabled = enabled && chkUseRandomSameSexMarriages.isSelected(); - final boolean percentageEnabled = method.isPercentage(); - chkUseRandomSameSexMarriages.setEnabled(enabled); - chkUseRandomClanPersonnelMarriages - .setEnabled(enabled && chkUseClanPersonnelMarriages.isSelected()); + final boolean percentageEnabled = method.isDiceRoll(); + chkUseRandomClanPersonnelMarriages.setEnabled(enabled && chkUseClanPersonnelMarriages.isSelected()); chkUseRandomPrisonerMarriages.setEnabled(enabled && chkUsePrisonerMarriages.isSelected()); lblRandomMarriageAgeRange.setEnabled(enabled); spnRandomMarriageAgeRange.setEnabled(enabled); percentageRandomMarriagePanel.setEnabled(percentageEnabled); - lblPercentageRandomMarriageSameSexChance.setEnabled(sameSexEnabled && percentageEnabled); - spnPercentageRandomMarriageSameSexChance.setEnabled(sameSexEnabled && percentageEnabled); - }); - - chkUseRandomSameSexMarriages = new JCheckBox(resources.getString("chkUseRandomSameSexMarriages.text")); - chkUseRandomSameSexMarriages - .setToolTipText(resources.getString("chkUseRandomSameSexMarriages.toolTipText")); - chkUseRandomSameSexMarriages.setName("chkUseRandomSameSexMarriages"); - chkUseRandomSameSexMarriages.addActionListener(evt -> { - final RandomMarriageMethod method = comboRandomMarriageMethod.getSelectedItem(); - if (method == null) { - return; - } - final boolean sameSexEnabled = chkUseRandomSameSexMarriages.isEnabled() - && chkUseRandomSameSexMarriages.isSelected(); - final boolean percentageEnabled = sameSexEnabled && method.isPercentage(); - lblPercentageRandomMarriageSameSexChance.setEnabled(percentageEnabled); - spnPercentageRandomMarriageSameSexChance.setEnabled(percentageEnabled); }); chkUseRandomClanPersonnelMarriages = new JCheckBox( @@ -5943,9 +5943,7 @@ public Component getListCellRendererComponent(final JList list, final Object layout.createSequentialGroup() .addGroup(layout.createParallelGroup(Alignment.BASELINE) .addComponent(lblRandomMarriageMethod) - .addComponent(comboRandomMarriageMethod, - Alignment.LEADING)) - .addComponent(chkUseRandomSameSexMarriages) + .addComponent(comboRandomMarriageMethod, Alignment.LEADING)) .addComponent(chkUseRandomClanPersonnelMarriages) .addComponent(chkUseRandomPrisonerMarriages) .addGroup(layout.createParallelGroup(Alignment.BASELINE) @@ -5959,7 +5957,6 @@ public Component getListCellRendererComponent(final JList list, final Object .addGroup(layout.createSequentialGroup() .addComponent(lblRandomMarriageMethod) .addComponent(comboRandomMarriageMethod)) - .addComponent(chkUseRandomSameSexMarriages) .addComponent(chkUseRandomClanPersonnelMarriages) .addComponent(chkUseRandomPrisonerMarriages) .addGroup(layout.createSequentialGroup() @@ -5972,40 +5969,36 @@ public Component getListCellRendererComponent(final JList list, final Object private void createPercentageRandomMarriagePanel(final JPanel panel) { // Create Panel Components - final JLabel lblPercentageRandomMarriageOppositeSexChance = new JLabel( - resources.getString("lblPercentageRandomMarriageOppositeSexChance.text")); - lblPercentageRandomMarriageOppositeSexChance - .setToolTipText(resources - .getString("lblPercentageRandomMarriageOppositeSexChance.toolTipText")); - lblPercentageRandomMarriageOppositeSexChance.setName("lblPercentageRandomMarriageOppositeSexChance"); + final JLabel lblRandomMarriageOppositeSexDiceSize = new JLabel(resources.getString("lblRandomMarriageOppositeSexDiceSize.text")); + lblRandomMarriageOppositeSexDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomMarriageOppositeSexDiceSize.toolTipText"))); + lblRandomMarriageOppositeSexDiceSize.setName("lblRandomMarriageOppositeSexDiceSize"); - spnPercentageRandomMarriageOppositeSexChance = new JSpinner(new SpinnerNumberModel(0, 0, 100, 0.001)); - spnPercentageRandomMarriageOppositeSexChance - .setToolTipText(resources - .getString("lblPercentageRandomMarriageOppositeSexChance.toolTipText")); - spnPercentageRandomMarriageOppositeSexChance.setName("spnPercentageRandomMarriageOppositeSexChance"); + spnRandomMarriageDiceSize = new JSpinner(new SpinnerNumberModel(542, 0, 100000, 1)); + spnRandomMarriageDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomMarriageOppositeSexDiceSize.toolTipText"))); + spnRandomMarriageDiceSize.setName("spnPercentageRandomMarriageOppositeSexChance"); - lblPercentageRandomMarriageSameSexChance = new JLabel( - resources.getString("lblPercentageRandomMarriageSameSexChance.text")); - lblPercentageRandomMarriageSameSexChance - .setToolTipText(resources - .getString("lblPercentageRandomMarriageSameSexChance.toolTipText")); - lblPercentageRandomMarriageSameSexChance.setName("lblPercentageRandomMarriageSameSexChance"); + final JLabel lblRandomSameSexMarriageDiceSize = new JLabel(resources.getString("lblRandomSameSexMarriageDiceSize.text")); + lblRandomSameSexMarriageDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomSameSexMarriageDiceSize.toolTipText"))); + lblRandomSameSexMarriageDiceSize.setName("lblRandomSameSexMarriageDiceSize"); - spnPercentageRandomMarriageSameSexChance = new JSpinner(new SpinnerNumberModel(0, 0, 100, 0.001)); - spnPercentageRandomMarriageSameSexChance - .setToolTipText(resources - .getString("lblPercentageRandomMarriageSameSexChance.toolTipText")); - spnPercentageRandomMarriageSameSexChance.setName("spnPercentageRandomMarriageSameSexChance"); + spnRandomSameSexMarriageDiceSize = new JSpinner(new SpinnerNumberModel(542, 0, 100000, 1)); + spnRandomSameSexMarriageDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomSameSexMarriageDiceSize.toolTipText"))); + spnRandomSameSexMarriageDiceSize.setName("spnRandomSameSexMarriageDiceSize"); + + final JLabel lblRandomNewDependentMarriage = new JLabel(resources.getString("lblRandomNewDependentMarriage.text")); + lblRandomNewDependentMarriage.setToolTipText(wordWrap(resources.getString("lblRandomNewDependentMarriage.toolTipText"))); + lblRandomNewDependentMarriage.setName("lblRandomNewDependentMarriage"); + + spnRandomNewDependentMarriage = new JSpinner(new SpinnerNumberModel(542, 0, 100000, 1)); + spnRandomNewDependentMarriage.setToolTipText(wordWrap(resources.getString("lblRandomNewDependentMarriage.toolTipText"))); + spnRandomNewDependentMarriage.setName("spnRandomNewDependentMarriage"); // Programmatically Assign Accessibility Labels - lblPercentageRandomMarriageOppositeSexChance.setLabelFor(spnPercentageRandomMarriageOppositeSexChance); - lblPercentageRandomMarriageSameSexChance.setLabelFor(spnPercentageRandomMarriageSameSexChance); + lblRandomMarriageOppositeSexDiceSize.setLabelFor(spnRandomMarriageDiceSize); // Layout the Panel - panel.setBorder(BorderFactory - .createTitledBorder(resources.getString("percentageRandomMarriagePanel.title"))); - panel.setToolTipText(RandomMarriageMethod.PERCENTAGE.getToolTipText()); + panel.setBorder(BorderFactory.createTitledBorder(resources.getString("percentageRandomMarriagePanel.title"))); + panel.setToolTipText(RandomMarriageMethod.DICE_ROLL.getToolTipText()); final GroupLayout layout = new GroupLayout(panel); layout.setAutoCreateGaps(true); @@ -6015,22 +6008,28 @@ private void createPercentageRandomMarriagePanel(final JPanel panel) { layout.setVerticalGroup( layout.createSequentialGroup() .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPercentageRandomMarriageOppositeSexChance) - .addComponent(spnPercentageRandomMarriageOppositeSexChance, - Alignment.LEADING)) + .addComponent(lblRandomMarriageOppositeSexDiceSize) + .addComponent(spnRandomMarriageDiceSize, Alignment.LEADING)) .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPercentageRandomMarriageSameSexChance) - .addComponent(spnPercentageRandomMarriageSameSexChance, - Alignment.LEADING))); + .addComponent(lblRandomSameSexMarriageDiceSize) + .addComponent(spnRandomSameSexMarriageDiceSize, Alignment.LEADING)) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblRandomSameSexMarriageDiceSize) + .addComponent(spnRandomNewDependentMarriage, Alignment.LEADING)) + ); layout.setHorizontalGroup( layout.createParallelGroup(Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(lblPercentageRandomMarriageOppositeSexChance) - .addComponent(spnPercentageRandomMarriageOppositeSexChance)) + .addComponent(lblRandomMarriageOppositeSexDiceSize) + .addComponent(spnRandomMarriageDiceSize)) + .addGroup(layout.createSequentialGroup() + .addComponent(lblRandomSameSexMarriageDiceSize) + .addComponent(spnRandomSameSexMarriageDiceSize)) .addGroup(layout.createSequentialGroup() - .addComponent(lblPercentageRandomMarriageSameSexChance) - .addComponent(spnPercentageRandomMarriageSameSexChance))); + .addComponent(lblRandomSameSexMarriageDiceSize) + .addComponent(spnRandomNewDependentMarriage)) + ); } private JPanel createDivorcePanel() { @@ -6154,18 +6153,14 @@ public Component getListCellRendererComponent(final JList list, final Object return; } final boolean enabled = !method.isNone(); - final boolean oppositeSexEnabled = enabled && chkUseRandomOppositeSexDivorce.isSelected(); - final boolean sameSexEnabled = enabled && chkUseRandomSameSexDivorce.isSelected(); - final boolean percentageEnabled = method.isPercentage(); + final boolean percentageEnabled = method.isDiceRoll(); chkUseRandomOppositeSexDivorce.setEnabled(enabled); chkUseRandomSameSexDivorce.setEnabled(enabled); chkUseRandomClanPersonnelDivorce.setEnabled(enabled && chkUseClanPersonnelDivorce.isSelected()); chkUseRandomPrisonerDivorce.setEnabled(enabled && chkUsePrisonerDivorce.isSelected()); percentageRandomDivorcePanel.setEnabled(percentageEnabled); - lblPercentageRandomDivorceOppositeSexChance.setEnabled(oppositeSexEnabled && percentageEnabled); - spnPercentageRandomDivorceOppositeSexChance.setEnabled(oppositeSexEnabled && percentageEnabled); - lblPercentageRandomDivorceSameSexChance.setEnabled(sameSexEnabled && percentageEnabled); - spnPercentageRandomDivorceSameSexChance.setEnabled(sameSexEnabled && percentageEnabled); + lblRandomDivorceDiceSize.setEnabled(percentageEnabled); + spnRandomDivorceDiceSize.setEnabled(percentageEnabled); }); chkUseRandomOppositeSexDivorce = new JCheckBox( @@ -6173,33 +6168,11 @@ public Component getListCellRendererComponent(final JList list, final Object chkUseRandomOppositeSexDivorce .setToolTipText(resources.getString("chkUseRandomOppositeSexDivorce.toolTipText")); chkUseRandomOppositeSexDivorce.setName("chkUseRandomOppositeSexDivorce"); - chkUseRandomOppositeSexDivorce.addActionListener(evt -> { - final RandomDivorceMethod method = comboRandomDivorceMethod.getSelectedItem(); - if (method == null) { - return; - } - final boolean selected = chkUseRandomOppositeSexDivorce.isEnabled() - && chkUseRandomOppositeSexDivorce.isSelected(); - final boolean percentageEnabled = selected && method.isPercentage(); - lblPercentageRandomDivorceOppositeSexChance.setEnabled(percentageEnabled); - spnPercentageRandomDivorceOppositeSexChance.setEnabled(percentageEnabled); - }); chkUseRandomSameSexDivorce = new JCheckBox(resources.getString("chkUseRandomSameSexDivorce.text")); chkUseRandomSameSexDivorce .setToolTipText(resources.getString("chkUseRandomSameSexDivorce.toolTipText")); chkUseRandomSameSexDivorce.setName("chkUseRandomSameSexDivorce"); - chkUseRandomSameSexDivorce.addActionListener(evt -> { - final RandomDivorceMethod method = comboRandomDivorceMethod.getSelectedItem(); - if (method == null) { - return; - } - final boolean selected = chkUseRandomSameSexDivorce.isEnabled() - && chkUseRandomSameSexDivorce.isSelected(); - final boolean percentageEnabled = selected && method.isPercentage(); - lblPercentageRandomDivorceSameSexChance.setEnabled(percentageEnabled); - spnPercentageRandomDivorceSameSexChance.setEnabled(percentageEnabled); - }); chkUseRandomClanPersonnelDivorce = new JCheckBox( resources.getString("chkUseRandomClanPersonnelDivorce.text")); @@ -6255,44 +6228,20 @@ public Component getListCellRendererComponent(final JList list, final Object private void createPercentageRandomDivorcePanel(final JPanel panel) { // Create Panel Components - lblPercentageRandomDivorceOppositeSexChance = new JLabel( - resources.getString("lblPercentageRandomDivorceOppositeSexChance.text")); - lblPercentageRandomDivorceOppositeSexChance - .setToolTipText(resources - .getString("lblPercentageRandomDivorceOppositeSexChance.toolTipText")); - lblPercentageRandomDivorceOppositeSexChance.setName("lblPercentageRandomDivorceOppositeSexChance"); + lblRandomDivorceDiceSize = new JLabel(resources.getString("lblRandomDivorceDiceSize.text")); + lblRandomDivorceDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomDivorceDiceSize.toolTipText"))); + lblRandomDivorceDiceSize.setName("lblRandomDivorceDiceSize"); - spnPercentageRandomDivorceOppositeSexChance = new JSpinner(new SpinnerNumberModel(0, 0, 100, 0.00001)); - spnPercentageRandomDivorceOppositeSexChance - .setToolTipText(resources - .getString("lblPercentageRandomDivorceOppositeSexChance.toolTipText")); - spnPercentageRandomDivorceOppositeSexChance.setName("spnPercentageRandomDivorceOppositeSexChance"); - spnPercentageRandomDivorceOppositeSexChance - .setEditor(new NumberEditor(spnPercentageRandomDivorceOppositeSexChance, "0.00000")); - - lblPercentageRandomDivorceSameSexChance = new JLabel( - resources.getString("lblPercentageRandomDivorceSameSexChance.text")); - lblPercentageRandomDivorceSameSexChance - .setToolTipText(resources - .getString("lblPercentageRandomDivorceSameSexChance.toolTipText")); - lblPercentageRandomDivorceSameSexChance.setName("lblPercentageRandomDivorceSameSexChance"); - - spnPercentageRandomDivorceSameSexChance = new JSpinner(new SpinnerNumberModel(0, 0, 100, 0.00001)); - spnPercentageRandomDivorceSameSexChance - .setToolTipText(resources - .getString("lblPercentageRandomDivorceSameSexChance.toolTipText")); - spnPercentageRandomDivorceSameSexChance.setName("spnPercentageRandomDivorceSameSexChance"); - spnPercentageRandomDivorceSameSexChance - .setEditor(new NumberEditor(spnPercentageRandomDivorceSameSexChance, "0.00000")); + spnRandomDivorceDiceSize = new JSpinner(new SpinnerNumberModel(3221, 0, 100000, 1)); + spnRandomDivorceDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomDivorceDiceSize.toolTipText"))); + spnRandomDivorceDiceSize.setName("spnRandomDivorceDiceSize"); // Programmatically Assign Accessibility Labels - lblPercentageRandomDivorceOppositeSexChance.setLabelFor(spnPercentageRandomDivorceOppositeSexChance); - lblPercentageRandomDivorceSameSexChance.setLabelFor(spnPercentageRandomDivorceSameSexChance); + lblRandomDivorceDiceSize.setLabelFor(spnRandomDivorceDiceSize); // Layout the Panel - panel.setBorder(BorderFactory - .createTitledBorder(resources.getString("percentageRandomDivorcePanel.title"))); - panel.setToolTipText(RandomDivorceMethod.PERCENTAGE.getToolTipText()); + panel.setBorder(BorderFactory.createTitledBorder(resources.getString("percentageRandomDivorcePanel.title"))); + panel.setToolTipText(RandomDivorceMethod.DICE_ROLL.getToolTipText()); final GroupLayout layout = new GroupLayout(panel); layout.setAutoCreateGaps(true); @@ -6302,22 +6251,16 @@ private void createPercentageRandomDivorcePanel(final JPanel panel) { layout.setVerticalGroup( layout.createSequentialGroup() .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPercentageRandomDivorceOppositeSexChance) - .addComponent(spnPercentageRandomDivorceOppositeSexChance, - Alignment.LEADING)) - .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPercentageRandomDivorceSameSexChance) - .addComponent(spnPercentageRandomDivorceSameSexChance, - Alignment.LEADING))); + .addComponent(lblRandomDivorceDiceSize) + .addComponent(spnRandomDivorceDiceSize, Alignment.LEADING)) + ); layout.setHorizontalGroup( layout.createParallelGroup(Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(lblPercentageRandomDivorceOppositeSexChance) - .addComponent(spnPercentageRandomDivorceOppositeSexChance)) - .addGroup(layout.createSequentialGroup() - .addComponent(lblPercentageRandomDivorceSameSexChance) - .addComponent(spnPercentageRandomDivorceSameSexChance))); + .addComponent(lblRandomDivorceDiceSize) + .addComponent(spnRandomDivorceDiceSize)) + ); } private JPanel createProcreationPanel() { @@ -6410,6 +6353,18 @@ public Component getListCellRendererComponent(final JList list, final Object chkDisplayTrueDueDate.setToolTipText(resources.getString("chkDisplayTrueDueDate.toolTipText")); chkDisplayTrueDueDate.setName("chkDisplayTrueDueDate"); + final JLabel lblNoInterestInChildrenDiceSize = new JLabel(resources.getString("lblNoInterestInChildrenDiceSize.text")); + lblNoInterestInChildrenDiceSize.setToolTipText(wordWrap(resources.getString("lblNoInterestInChildrenDiceSize.toolTipText"))); + lblNoInterestInChildrenDiceSize.setName("lblNoInterestInChildrenDiceSize"); + + spnNoInterestInChildrenDiceSize = new JSpinner(new SpinnerNumberModel(3, 1, 100000, 1)); + spnNoInterestInChildrenDiceSize.setToolTipText(wordWrap(resources.getString("lblNoInterestInChildrenDiceSize.toolTipText"))); + spnNoInterestInChildrenDiceSize.setName("spnNoInterestInChildrenDiceSize"); + + chkUseMaternityLeave = new JCheckBox(resources.getString("chkUseMaternityLeave.text")); + chkUseMaternityLeave.setToolTipText(wordWrap(resources.getString("chkUseMaternityLeave.toolTipText"))); + chkUseMaternityLeave.setName("chkUseMaternityLeave"); + chkLogProcreation = new JCheckBox(resources.getString("chkLogProcreation.text")); chkLogProcreation.setToolTipText(resources.getString("chkLogProcreation.toolTipText")); chkLogProcreation.setName("chkLogProcreation"); @@ -6447,6 +6402,10 @@ public Component getListCellRendererComponent(final JList list, final Object .addComponent(chkAssignChildrenOfFoundersFounderTag) .addComponent(chkDetermineFatherAtBirth) .addComponent(chkDisplayTrueDueDate) + .addGroup(layout.createParallelGroup(Alignment.BASELINE) + .addComponent(lblNoInterestInChildrenDiceSize) + .addComponent(spnNoInterestInChildrenDiceSize, Alignment.LEADING)) + .addComponent(chkUseMaternityLeave) .addComponent(chkLogProcreation) .addComponent(randomProcreationPanel)); @@ -6466,6 +6425,10 @@ public Component getListCellRendererComponent(final JList list, final Object .addComponent(chkAssignChildrenOfFoundersFounderTag) .addComponent(chkDetermineFatherAtBirth) .addComponent(chkDisplayTrueDueDate) + .addGroup(layout.createSequentialGroup() + .addComponent(lblNoInterestInChildrenDiceSize) + .addComponent(spnNoInterestInChildrenDiceSize)) + .addComponent(chkUseMaternityLeave) .addComponent(chkLogProcreation) .addComponent(randomProcreationPanel)); @@ -6869,18 +6832,15 @@ public Component getListCellRendererComponent(final JList list, final Object return; } final boolean enabled = !method.isNone(); - final boolean percentageEnabled = method.isPercentage(); - final boolean relationshiplessEnabled = enabled - && chkUseRelationshiplessRandomProcreation.isSelected(); + final boolean percentageEnabled = method.isDiceRoll(); + final boolean relationshiplessEnabled = enabled && chkUseRelationshiplessRandomProcreation.isSelected(); chkUseRelationshiplessRandomProcreation.setEnabled(enabled); chkUseRandomClanPersonnelProcreation .setEnabled(enabled && chkUseClanPersonnelProcreation.isSelected()); chkUseRandomPrisonerProcreation.setEnabled(enabled && chkUsePrisonerProcreation.isSelected()); percentageRandomProcreationPanel.setEnabled(percentageEnabled); - lblPercentageRandomProcreationRelationshiplessChance - .setEnabled(relationshiplessEnabled && percentageEnabled); - spnPercentageRandomProcreationRelationshiplessChance - .setEnabled(relationshiplessEnabled && percentageEnabled); + lblRandomProcreationRelationshiplessDiceSize.setEnabled(relationshiplessEnabled && percentageEnabled); + spnRandomProcreationRelationshiplessDiceSize.setEnabled(relationshiplessEnabled && percentageEnabled); }); chkUseRelationshiplessRandomProcreation = new JCheckBox( @@ -6896,9 +6856,9 @@ public Component getListCellRendererComponent(final JList list, final Object } final boolean sameSexEnabled = chkUseRelationshiplessRandomProcreation.isEnabled() && chkUseRelationshiplessRandomProcreation.isSelected(); - final boolean percentageEnabled = sameSexEnabled && method.isPercentage(); - lblPercentageRandomProcreationRelationshiplessChance.setEnabled(percentageEnabled); - spnPercentageRandomProcreationRelationshiplessChance.setEnabled(percentageEnabled); + final boolean percentageEnabled = sameSexEnabled && method.isDiceRoll(); + lblRandomProcreationRelationshiplessDiceSize.setEnabled(percentageEnabled); + spnRandomProcreationRelationshiplessDiceSize.setEnabled(percentageEnabled); }); chkUseRandomClanPersonnelProcreation = new JCheckBox( @@ -6955,49 +6915,29 @@ public Component getListCellRendererComponent(final JList list, final Object private void createPercentageRandomProcreationPanel(final JPanel panel) { // Create Panel Components - final JLabel lblPercentageRandomProcreationRelationshipChance = new JLabel( - resources.getString("lblPercentageRandomProcreationRelationshipChance.text")); - lblPercentageRandomProcreationRelationshipChance - .setToolTipText(resources.getString( - "lblPercentageRandomProcreationRelationshipChance.toolTipText")); - lblPercentageRandomProcreationRelationshipChance - .setName("lblPercentageRandomProcreationRelationshipChance"); - - spnPercentageRandomProcreationRelationshipChance = new JSpinner( - new SpinnerNumberModel(0, 0, 100, 0.001)); - spnPercentageRandomProcreationRelationshipChance - .setToolTipText(resources.getString( - "lblPercentageRandomProcreationRelationshipChance.toolTipText")); - spnPercentageRandomProcreationRelationshipChance - .setName("spnPercentageRandomProcreationRelationshipChance"); - - lblPercentageRandomProcreationRelationshiplessChance = new JLabel( - resources.getString("lblPercentageRandomProcreationRelationshiplessChance.text")); - lblPercentageRandomProcreationRelationshiplessChance.setToolTipText( - resources.getString( - "lblPercentageRandomProcreationRelationshiplessChance.toolTipText")); - lblPercentageRandomProcreationRelationshiplessChance - .setName("lblPercentageRandomProcreationRelationshiplessChance"); - - spnPercentageRandomProcreationRelationshiplessChance = new JSpinner( - new SpinnerNumberModel(0, 0, 100, 0.001)); - spnPercentageRandomProcreationRelationshiplessChance.setToolTipText( - resources.getString( - "lblPercentageRandomProcreationRelationshiplessChance.toolTipText")); - spnPercentageRandomProcreationRelationshiplessChance - .setName("spnPercentageRandomProcreationRelationshiplessChance"); + final JLabel lblRandomProcreationRelationshipDiceSize = new JLabel(resources.getString("lblRandomProcreationRelationshipDiceSize.text")); + lblRandomProcreationRelationshipDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomProcreationRelationshipDiceSize.toolTipText"))); + lblRandomProcreationRelationshipDiceSize.setName("lblRandomProcreationRelationshipDiceSize"); + + spnRandomProcreationRelationshipDiceSize = new JSpinner(new SpinnerNumberModel(621, 0, 100000, 1)); + spnRandomProcreationRelationshipDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomProcreationRelationshipDiceSize.toolTipText"))); + spnRandomProcreationRelationshipDiceSize.setName("spnRandomProcreationRelationshipDiceSize"); + + lblRandomProcreationRelationshiplessDiceSize = new JLabel(resources.getString("lblRandomProcreationRelationshiplessDiceSize.text")); + lblRandomProcreationRelationshiplessDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomProcreationRelationshiplessDiceSize.toolTipText"))); + lblRandomProcreationRelationshiplessDiceSize.setName("lblRandomProcreationRelationshiplessDiceSize"); + + spnRandomProcreationRelationshiplessDiceSize = new JSpinner(new SpinnerNumberModel(1861, 0, 100000, 1)); + spnRandomProcreationRelationshiplessDiceSize.setToolTipText(wordWrap(resources.getString("lblRandomProcreationRelationshipDiceSize.toolTipText"))); + spnRandomProcreationRelationshiplessDiceSize.setName("spnRandomProcreationRelationshiplessDiceSize"); // Programmatically Assign Accessibility Labels - lblPercentageRandomProcreationRelationshipChance - .setLabelFor(spnPercentageRandomProcreationRelationshipChance); - lblPercentageRandomProcreationRelationshiplessChance - .setLabelFor(spnPercentageRandomProcreationRelationshiplessChance); + lblRandomProcreationRelationshipDiceSize.setLabelFor(spnRandomProcreationRelationshipDiceSize); + lblRandomProcreationRelationshiplessDiceSize.setLabelFor(spnRandomProcreationRelationshipDiceSize); // Layout the Panel - panel.setBorder( - BorderFactory.createTitledBorder( - resources.getString("percentageRandomProcreationPanel.title"))); - panel.setToolTipText(RandomProcreationMethod.PERCENTAGE.getToolTipText()); + panel.setBorder(BorderFactory.createTitledBorder(resources.getString("percentageRandomProcreationPanel.title"))); + panel.setToolTipText(RandomProcreationMethod.DICE_ROLL.getToolTipText()); final GroupLayout layout = new GroupLayout(panel); layout.setAutoCreateGaps(true); @@ -7007,22 +6947,22 @@ private void createPercentageRandomProcreationPanel(final JPanel panel) { layout.setVerticalGroup( layout.createSequentialGroup() .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPercentageRandomProcreationRelationshipChance) - .addComponent(spnPercentageRandomProcreationRelationshipChance, - Alignment.LEADING)) + .addComponent(lblRandomProcreationRelationshipDiceSize) + .addComponent(spnRandomProcreationRelationshipDiceSize, Alignment.LEADING)) .addGroup(layout.createParallelGroup(Alignment.BASELINE) - .addComponent(lblPercentageRandomProcreationRelationshiplessChance) - .addComponent(spnPercentageRandomProcreationRelationshiplessChance, - Alignment.LEADING))); + .addComponent(lblRandomProcreationRelationshiplessDiceSize) + .addComponent(spnRandomProcreationRelationshiplessDiceSize, Alignment.LEADING)) + ); layout.setHorizontalGroup( layout.createParallelGroup(Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(lblPercentageRandomProcreationRelationshipChance) - .addComponent(spnPercentageRandomProcreationRelationshipChance)) + .addComponent(lblRandomProcreationRelationshipDiceSize) + .addComponent(spnRandomProcreationRelationshipDiceSize)) .addGroup(layout.createSequentialGroup() - .addComponent(lblPercentageRandomProcreationRelationshiplessChance) - .addComponent(spnPercentageRandomProcreationRelationshiplessChance))); + .addComponent(lblRandomProcreationRelationshiplessDiceSize) + .addComponent(spnRandomProcreationRelationshiplessDiceSize)) + ); } private JPanel createDeathPanel() { @@ -7212,9 +7152,8 @@ private void createPercentageRandomDeathPanel(final JPanel panel) { lblPercentageRandomDeathChance.setLabelFor(spnPercentageRandomDeathChance); // Layout the Panel - panel.setBorder(BorderFactory - .createTitledBorder(resources.getString("percentageRandomDeathPanel.title"))); - panel.setToolTipText(RandomProcreationMethod.PERCENTAGE.getToolTipText()); + panel.setBorder(BorderFactory.createTitledBorder(resources.getString("percentageRandomDeathPanel.title"))); + panel.setToolTipText(RandomProcreationMethod.DICE_ROLL.getToolTipText()); final GroupLayout layout = new GroupLayout(panel); layout.setAutoCreateGaps(true); @@ -8681,10 +8620,14 @@ public void setOptions(@Nullable CampaignOptions options, // region Life Paths Tab // Personnel Randomization chkUseDylansRandomXP.setSelected(options.isUseDylansRandomXP()); + spnNonBinaryDiceSize.setValue(options.getNonBinaryDiceSize()); + + // Random Histories randomOriginOptionsPanel.setOptions(options.getRandomOriginOptions()); chkUseRandomPersonalities.setSelected(options.isUseRandomPersonalities()); chkUseRandomPersonalityReputation.setSelected(options.isUseRandomPersonalityReputation()); chkUseIntelligenceXpMultiplier.setSelected(options.isUseIntelligenceXpMultiplier()); + chkUseSimulatedRelationships.setSelected(options.isUseSimulatedRelationships()); // Family comboFamilyDisplayLevel.setSelectedItem(options.getFamilyDisplayLevel()); @@ -8698,27 +8641,19 @@ public void setOptions(@Nullable CampaignOptions options, chkUseManualMarriages.setSelected(options.isUseManualMarriages()); chkUseClanPersonnelMarriages.setSelected(options.isUseClanPersonnelMarriages()); chkUsePrisonerMarriages.setSelected(options.isUsePrisonerMarriages()); - spnMinimumMarriageAge.setValue(options.getMinimumMarriageAge()); + spnNoInterestInMarriageDiceSize.setValue(options.getNoInterestInMarriageDiceSize()); spnCheckMutualAncestorsDepth.setValue(options.getCheckMutualAncestorsDepth()); chkLogMarriageNameChanges.setSelected(options.isLogMarriageNameChanges()); for (final Entry entry : spnMarriageSurnameWeights.entrySet()) { entry.getValue().setValue(options.getMarriageSurnameWeights().get(entry.getKey()) / 10.0); } comboRandomMarriageMethod.setSelectedItem(options.getRandomMarriageMethod()); - if (chkUseRandomSameSexMarriages.isSelected() != options.isUseRandomSameSexMarriages()) { - if (chkUseRandomSameSexMarriages.isEnabled()) { - chkUseRandomSameSexMarriages.doClick(); - } else { - chkUseRandomSameSexMarriages.setSelected(options.isUseRandomSameSexMarriages()); - } - } chkUseRandomClanPersonnelMarriages.setSelected(options.isUseRandomClanPersonnelMarriages()); chkUseRandomPrisonerMarriages.setSelected(options.isUseRandomPrisonerMarriages()); spnRandomMarriageAgeRange.setValue(options.getRandomMarriageAgeRange()); - spnPercentageRandomMarriageOppositeSexChance - .setValue(options.getPercentageRandomMarriageOppositeSexChance() * 100.0); - spnPercentageRandomMarriageSameSexChance - .setValue(options.getPercentageRandomMarriageSameSexChance() * 100.0); + spnRandomMarriageDiceSize.setValue(options.getRandomMarriageDiceSize()); + spnRandomSameSexMarriageDiceSize.setValue(options.getRandomSameSexMarriageDiceSize()); + spnRandomNewDependentMarriage.setValue(options.getRandomNewDependentMarriage()); // Divorce chkUseManualDivorce.setSelected(options.isUseManualDivorce()); @@ -8745,10 +8680,7 @@ public void setOptions(@Nullable CampaignOptions options, } chkUseRandomClanPersonnelDivorce.setSelected(options.isUseRandomClanPersonnelDivorce()); chkUseRandomPrisonerDivorce.setSelected(options.isUseRandomPrisonerDivorce()); - spnPercentageRandomDivorceOppositeSexChance - .setValue(options.getPercentageRandomDivorceOppositeSexChance() * 100.0); - spnPercentageRandomDivorceSameSexChance - .setValue(options.getPercentageRandomDivorceSameSexChance() * 100.0); + spnRandomDivorceDiceSize.setValue(options.getRandomDivorceDiceSize()); // Procreation chkUseManualProcreation.setSelected(options.isUseManualProcreation()); @@ -8760,6 +8692,8 @@ public void setOptions(@Nullable CampaignOptions options, chkAssignChildrenOfFoundersFounderTag.setSelected(options.isAssignChildrenOfFoundersFounderTag()); chkDetermineFatherAtBirth.setSelected(options.isDetermineFatherAtBirth()); chkDisplayTrueDueDate.setSelected(options.isDisplayTrueDueDate()); + spnNoInterestInChildrenDiceSize.setValue(options.getNoInterestInChildrenDiceSize()); + chkUseMaternityLeave.setSelected(options.isUseMaternityLeave()); chkLogProcreation.setSelected(options.isLogProcreation()); comboRandomProcreationMethod.setSelectedItem(options.getRandomProcreationMethod()); if (chkUseRelationshiplessRandomProcreation.isSelected() != options @@ -8773,10 +8707,8 @@ public void setOptions(@Nullable CampaignOptions options, } chkUseRandomClanPersonnelProcreation.setSelected(options.isUseRandomClanPersonnelProcreation()); chkUseRandomPrisonerProcreation.setSelected(options.isUseRandomPrisonerProcreation()); - spnPercentageRandomProcreationRelationshipChance - .setValue(options.getPercentageRandomProcreationRelationshipChance() * 100.0); - spnPercentageRandomProcreationRelationshiplessChance - .setValue(options.getPercentageRandomProcreationRelationshiplessChance() * 100.0); + spnRandomProcreationRelationshipDiceSize.setValue(options.getRandomProcreationRelationshipDiceSize()); + spnRandomProcreationRelationshiplessDiceSize.setValue(options.getRandomProcreationRelationshiplessDiceSize()); // Education chkUseEducationModule.setSelected(options.isUseEducationModule()); @@ -9430,8 +9362,12 @@ public void updateOptions() { // region Life Paths Tab // Personnel Randomization options.setUseDylansRandomXP(chkUseDylansRandomXP.isSelected()); + options.setNonBinaryDiceSize((int) spnNonBinaryDiceSize.getValue()); + + // Random Histories options.setRandomOriginOptions(randomOriginOptionsPanel.createOptionsFromPanel()); options.setUseRandomPersonalities(chkUseRandomPersonalities.isSelected()); + options.setUseSimulatedRelationships(chkUseSimulatedRelationships.isSelected()); options.setUseRandomPersonalityReputation(chkUseRandomPersonalityReputation.isSelected()); options.setUseIntelligenceXpMultiplier(chkUseIntelligenceXpMultiplier.isSelected()); @@ -9447,7 +9383,7 @@ public void updateOptions() { options.setUseManualMarriages(chkUseManualMarriages.isSelected()); options.setUseClanPersonnelMarriages(chkUseClanPersonnelMarriages.isSelected()); options.setUsePrisonerMarriages(chkUsePrisonerMarriages.isSelected()); - options.setMinimumMarriageAge((Integer) spnMinimumMarriageAge.getValue()); + options.setNoInterestInMarriageDiceSize((int) spnNoInterestInMarriageDiceSize.getValue()); options.setCheckMutualAncestorsDepth((Integer) spnCheckMutualAncestorsDepth.getValue()); options.setLogMarriageNameChanges(chkLogMarriageNameChanges.isSelected()); for (final Entry entry : spnMarriageSurnameWeights.entrySet()) { @@ -9455,14 +9391,12 @@ public void updateOptions() { (int) Math.round((Double) entry.getValue().getValue() * 10.0)); } options.setRandomMarriageMethod(comboRandomMarriageMethod.getSelectedItem()); - options.setUseRandomSameSexMarriages(chkUseRandomSameSexMarriages.isSelected()); options.setUseRandomClanPersonnelMarriages(chkUseRandomClanPersonnelMarriages.isSelected()); options.setUseRandomPrisonerMarriages(chkUseRandomPrisonerMarriages.isSelected()); options.setRandomMarriageAgeRange((Integer) spnRandomMarriageAgeRange.getValue()); - options.setPercentageRandomMarriageOppositeSexChance( - (Double) spnPercentageRandomMarriageOppositeSexChance.getValue() / 100.0); - options.setPercentageRandomMarriageSameSexChance( - (Double) spnPercentageRandomMarriageSameSexChance.getValue() / 100.0); + options.setRandomMarriageDiceSize((int) spnRandomMarriageDiceSize.getValue()); + options.setRandomSameSexMarriageDiceSize((int) spnRandomSameSexMarriageDiceSize.getValue()); + options.setRandomNewDependentMarriage((int) spnRandomNewDependentMarriage.getValue()); // Divorce options.setUseManualDivorce(chkUseManualDivorce.isSelected()); @@ -9477,10 +9411,7 @@ public void updateOptions() { options.setUseRandomSameSexDivorce(chkUseRandomSameSexDivorce.isSelected()); options.setUseRandomClanPersonnelDivorce(chkUseRandomClanPersonnelDivorce.isSelected()); options.setUseRandomPrisonerDivorce(chkUseRandomPrisonerDivorce.isSelected()); - options.setPercentageRandomDivorceOppositeSexChance( - (Double) spnPercentageRandomDivorceOppositeSexChance.getValue() / 100.0); - options.setPercentageRandomDivorceSameSexChance( - (Double) spnPercentageRandomDivorceSameSexChance.getValue() / 100.0); + options.setRandomDivorceDiceSize((int) spnRandomDivorceDiceSize.getValue()); // Procreation options.setUseManualProcreation(chkUseManualProcreation.isSelected()); @@ -9493,17 +9424,16 @@ public void updateOptions() { chkAssignChildrenOfFoundersFounderTag.isSelected()); options.setDetermineFatherAtBirth(chkDetermineFatherAtBirth.isSelected()); options.setDisplayTrueDueDate(chkDisplayTrueDueDate.isSelected()); + options.setNoInterestInChildrenDiceSize((int) spnNoInterestInChildrenDiceSize.getValue()); + options.setUseMaternityLeave(chkUseMaternityLeave.isSelected()); options.setLogProcreation(chkLogProcreation.isSelected()); options.setRandomProcreationMethod(comboRandomProcreationMethod.getSelectedItem()); options.setUseRelationshiplessRandomProcreation( chkUseRelationshiplessRandomProcreation.isSelected()); options.setUseRandomClanPersonnelProcreation(chkUseRandomClanPersonnelProcreation.isSelected()); options.setUseRandomPrisonerProcreation(chkUseRandomPrisonerProcreation.isSelected()); - options.setPercentageRandomProcreationRelationshipChance( - (Double) spnPercentageRandomProcreationRelationshipChance.getValue() / 100.0); - options.setPercentageRandomProcreationRelationshiplessChance( - (Double) spnPercentageRandomProcreationRelationshiplessChance.getValue() - / 100.0); + options.setRandomProcreationRelationshipDiceSize((Integer) spnRandomProcreationRelationshipDiceSize.getValue()); + options.setRandomProcreationRelationshiplessDiceSize((Integer) spnRandomProcreationRelationshiplessDiceSize.getValue()); // Education options.setUseEducationModule(chkUseEducationModule.isSelected()); @@ -10137,7 +10067,7 @@ public int getRowHeight(int row) { /* * This table does not use any data from the main TableModel, - * so just return a value based on the row parameter. + * so return a value based on the row parameter. */ @Override public Object getValueAt(int row, int column) { diff --git a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java index 17f1178d0f..3af691a61e 100644 --- a/MekHQ/src/mekhq/gui/view/PersonViewPanel.java +++ b/MekHQ/src/mekhq/gui/view/PersonViewPanel.java @@ -967,7 +967,7 @@ private JPanel fillFamily() { lblSpouse2.setName("lblSpouse2"); lblSpouse1.setLabelFor(lblSpouse2); - lblSpouse2.setText(String.format("%s", spouse.getHyperlinkedName())); + lblSpouse2.setText(String.format("%s", spouse.getHyperlinkedFullTitle())); lblSpouse2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); lblSpouse2.addMouseListener(new MouseAdapter() { @Override @@ -997,16 +997,21 @@ public void mouseClicked(MouseEvent e) { gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new Insets(0, 10, 0, 0); - for (FormerSpouse formerSpouse : person.getGenealogy().getFormerSpouses()) { + List formerSpouses = person.getGenealogy().getFormerSpouses(); + Collections.reverse(person.getGenealogy().getFormerSpouses()); + + for (FormerSpouse formerSpouse : formerSpouses) { Person ex = formerSpouse.getFormerSpouse(); + String name = getRelativeName(ex); + gridBagConstraints.gridy = firsty; lblFormerSpouses2 = new JLabel(); lblFormerSpouses2.setName("lblFormerSpouses2"); lblFormerSpouses2.getAccessibleContext().getAccessibleRelationSet().add( new AccessibleRelation(AccessibleRelation.LABELED_BY, lblFormerSpouses1)); lblFormerSpouses2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - lblFormerSpouses2.setText(String.format("%s, %s, %s", - ex.getFullName(), formerSpouse.getReason(), + lblFormerSpouses2.setText(String.format("%s, %s, %s", + name, formerSpouse.getReason(), MekHQ.getMHQOptions().getDisplayFormattedDate(formerSpouse.getDate()))); lblFormerSpouses2.addMouseListener(new MouseAdapter() { @Override @@ -1036,13 +1041,15 @@ public void mouseClicked(MouseEvent e) { gridBagConstraints.insets = new Insets(0, 10, 0, 0); for (Person child : children) { + String name = getRelativeName(child); + gridBagConstraints.gridy = firsty; lblChildren2 = new JLabel(); lblChildren2.setName("lblChildren2"); lblChildren2.getAccessibleContext().getAccessibleRelationSet().add( new AccessibleRelation(AccessibleRelation.LABELED_BY, lblChildren1)); lblChildren2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - lblChildren2.setText(String.format("%s", child.getFullName())); + lblChildren2.setText(String.format("%s", name)); lblChildren2.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { @@ -1074,14 +1081,15 @@ public void mouseClicked(MouseEvent e) { gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; for (Person grandchild : grandchildren) { + String name = getRelativeName(grandchild); + gridBagConstraints.gridy = firsty; lblGrandchildren2 = new JLabel(); lblGrandchildren2.setName("lblGrandchildren2"); lblGrandchildren2.getAccessibleContext().getAccessibleRelationSet().add( new AccessibleRelation(AccessibleRelation.LABELED_BY, lblGrandchildren1)); lblGrandchildren2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - lblGrandchildren2 - .setText(String.format("%s", grandchild.getFullName())); + lblGrandchildren2.setText(String.format("%s", name)); lblGrandchildren2.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { @@ -1141,8 +1149,10 @@ public void mouseClicked(MouseEvent e) { gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; for (Person sibling : siblings) { + String name = getRelativeName(sibling); + gridBagConstraints.gridy = firsty; - lblSiblings2 = new JLabel(String.format("%s", sibling.getHyperlinkedName())); + lblSiblings2 = new JLabel(String.format("%s", name)); lblSiblings2.setName("lblSiblings2"); lblSiblings2.getAccessibleContext().getAccessibleRelationSet().add( new AccessibleRelation(AccessibleRelation.LABELED_BY, lblSiblings1)); @@ -1179,9 +1189,11 @@ public void mouseClicked(MouseEvent e) { gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; for (Person grandparent : grandparents) { + String name = getRelativeName(grandparent); + gridBagConstraints.gridy = firsty; lblGrandparents2 = new JLabel(String.format("%s", - grandparent.getHyperlinkedName())); + name)); lblGrandparents2.setName("lblGrandparents2"); lblGrandparents2.getAccessibleContext().getAccessibleRelationSet().add( new AccessibleRelation(AccessibleRelation.LABELED_BY, lblGrandparents1)); @@ -1217,9 +1229,11 @@ public void mouseClicked(MouseEvent e) { gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; for (Person auntOrUncle : auntsAndUncles) { + String name = getRelativeName(auntOrUncle); + gridBagConstraints.gridy = firsty; lblAuntsOrUncles2 = new JLabel(String.format("%s", - auntOrUncle.getHyperlinkedName())); + name)); lblAuntsOrUncles2.setName("lblAuntsOrUncles2"); lblAuntsOrUncles2.getAccessibleContext().getAccessibleRelationSet().add( new AccessibleRelation(AccessibleRelation.LABELED_BY, lblAuntsOrUncles1)); @@ -1255,13 +1269,15 @@ public void mouseClicked(MouseEvent e) { gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; for (Person cousin : cousins) { + String name = getRelativeName(cousin); + gridBagConstraints.gridy = firsty; lblCousins2 = new JLabel(); lblCousins2.setName("lblCousins2"); lblCousins2.getAccessibleContext().getAccessibleRelationSet().add( new AccessibleRelation(AccessibleRelation.LABELED_BY, lblCousins1)); lblCousins2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); - lblCousins2.setText(String.format("%s", cousin.getHyperlinkedName())); + lblCousins2.setText(String.format("%s", name)); lblCousins2.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { @@ -1277,6 +1293,21 @@ public void mouseClicked(MouseEvent e) { return pnlFamily; } + /** + * If the relative has joined the campaign, the hyperlinked full title is returned. + * Otherwise, the full name of the relative is returned. + * + * @param relative The relative. + * @return The relative's name. + */ + private static String getRelativeName(Person relative) { + if (relative.getJoinedCampaign() == null) { + return relative.getFirstName(); + } else { + return "" + relative.getHyperlinkedFullTitle() + ""; + } + } + private JPanel fillSkills() { // skill panel JPanel pnlSkills = new JPanel(new GridBagLayout()); @@ -1882,7 +1913,7 @@ private JPanel fillInjuries() { /** * Gets the advanced medical effects active for the person. - * + * * @return an HTML encoded string of effects */ private String getAdvancedMedalEffectString(Person p) { diff --git a/MekHQ/unittests/mekhq/campaign/personnel/divorce/AbstractDivorceTest.java b/MekHQ/unittests/mekhq/campaign/personnel/divorce/AbstractDivorceTest.java index 3076dc8835..773a4198c6 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/divorce/AbstractDivorceTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/divorce/AbstractDivorceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -54,29 +54,6 @@ public void beforeEach() { lenient().when(mockCampaign.getCampaignOptions()).thenReturn(mockCampaignOptions); } - //region Getters/Setters - @Disabled // FIXME : Windchild : Test Missing - @Test - public void testGettersAndSetters() { -/* - when(mockCampaignOptions.isUseClanPersonnelDivorce()).thenReturn(false); - when(mockCampaignOptions.isUsePrisonerDivorce()).thenReturn(false); - when(mockCampaignOptions.isUseRandomSameSexDivorce()).thenReturn(false); - when(mockCampaignOptions.isUseRandomClanPersonnelDivorce()).thenReturn(false); - when(mockCampaignOptions.isUseRandomPrisonerDivorce()).thenReturn(false); - - final AbstractDivorce disabledDivorce = new DisabledRandomDivorce(mockCampaignOptions); - - assertEquals(RandomDivorceMethod.NONE, disabledDivorce.getMethod()); - assertFalse(disabledDivorce.isUseClanPersonnelDivorce()); - assertFalse(disabledDivorce.isUsePrisonerDivorce()); - assertFalse(disabledDivorce.isUseRandomSameSexDivorce()); - assertFalse(disabledDivorce.isUseRandomClanPersonnelDivorce()); - assertFalse(disabledDivorce.isUseRandomPrisonerDivorce()); -*/ - } - //endregion Getters/Setters - @Disabled // FIXME : Windchild : Test Missing @Test public void testCanDivorce() { @@ -125,7 +102,7 @@ public void testDivorce() { //region New Day @Disabled // FIXME : Windchild : Test Missing @Test - public void testProcessNewDay() { + public void testProcessNewWeek() { } //endregion New Day diff --git a/MekHQ/unittests/mekhq/campaign/personnel/divorce/DisabledRandomDivorceTest.java b/MekHQ/unittests/mekhq/campaign/personnel/divorce/DisabledRandomDivorceTest.java index f527aaee11..103c1b7c2d 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/divorce/DisabledRandomDivorceTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/divorce/DisabledRandomDivorceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -48,12 +48,7 @@ public void beforeEach() { } @Test - public void testRandomOppositeSexDivorce() { - assertFalse(new DisabledRandomDivorce(mockOptions).randomOppositeSexDivorce(mockPerson)); - } - - @Test - public void testRandomSameSexDivorce() { - assertFalse(new DisabledRandomDivorce(mockOptions).randomSameSexDivorce(mockPerson)); + public void testRandomDivorce() { + assertFalse(new DisabledRandomDivorce(mockOptions).randomDivorce()); } } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/divorce/PercentageRandomDivorceTest.java b/MekHQ/unittests/mekhq/campaign/personnel/divorce/PercentageRandomDivorceTest.java index 7ad64a7f29..15b7e0da1d 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/divorce/PercentageRandomDivorceTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/divorce/PercentageRandomDivorceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -49,41 +49,20 @@ public void beforeEach() { when(mockOptions.isUseRandomSameSexDivorce()).thenReturn(false); when(mockOptions.isUseRandomClanPersonnelDivorce()).thenReturn(false); when(mockOptions.isUseRandomPrisonerDivorce()).thenReturn(false); - when(mockOptions.getPercentageRandomDivorceOppositeSexChance()).thenReturn(0.5); - when(mockOptions.getPercentageRandomDivorceSameSexChance()).thenReturn(0.5); + when(mockOptions.getRandomDivorceDiceSize()).thenReturn(5); } @Test - public void testRandomOppositeSexDivorce() { - final PercentageRandomDivorce percentageRandomDivorce = new PercentageRandomDivorce(mockOptions); - // This ignores the person, so just using a mocked person - // Testing Minimum (0f), Below Value (0.49f), At Value (0.5f), and Maximum (1f) - try (MockedStatic compute = Mockito.mockStatic(Compute.class)) { - compute.when(Compute::randomFloat).thenReturn(0f); - assertTrue(percentageRandomDivorce.randomOppositeSexDivorce(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.49f); - assertTrue(percentageRandomDivorce.randomOppositeSexDivorce(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.5f); - assertFalse(percentageRandomDivorce.randomOppositeSexDivorce(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(1f); - assertFalse(percentageRandomDivorce.randomOppositeSexDivorce(mockPerson)); - } - } + public void testRandomDivorce() { + final RandomDivorce randomDivorce = new RandomDivorce(mockOptions); - @Test - public void testRandomSameSexDivorce() { - final PercentageRandomDivorce percentageRandomDivorce = new PercentageRandomDivorce(mockOptions); - // This ignores the person, so just using a mocked person - // Testing Minimum (0f), Below Value (0.49f), At Value (0.5f), and Maximum (1f) - try (MockedStatic compute = Mockito.mockStatic(Compute.class)) { - compute.when(Compute::randomFloat).thenReturn(0f); - assertTrue(percentageRandomDivorce.randomSameSexDivorce(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.49f); - assertTrue(percentageRandomDivorce.randomSameSexDivorce(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.5f); - assertFalse(percentageRandomDivorce.randomSameSexDivorce(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(1f); - assertFalse(percentageRandomDivorce.randomSameSexDivorce(mockPerson)); + int diceSize = 5; + + try(MockedStatic compute = Mockito.mockStatic(Compute.class)) { + compute.when(() -> Compute.randomInt(diceSize)).thenReturn(0); + assertTrue(randomDivorce.randomDivorce()); + compute.when(() -> Compute.randomInt(diceSize)).thenReturn(1); + assertFalse(randomDivorce.randomDivorce()); } } } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelStatusTest.java b/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelStatusTest.java index 44f5e4a624..fe59145da9 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelStatusTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/enums/PersonnelStatusTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -108,6 +108,17 @@ void testIsOnLeave() { } } + @Test + public void testIsOnMaternityLeave() { + for (final PersonnelStatus personnelStatus : statuses) { + if (personnelStatus == PersonnelStatus.ON_MATERNITY_LEAVE) { + assertTrue(personnelStatus.isOnMaternityLeave()); + } else { + assertFalse(personnelStatus.isOnMaternityLeave()); + } + } + } + @Test void testIsAwol() { for (final PersonnelStatus personnelStatus : statuses) { @@ -282,6 +293,7 @@ void testIsAbsent() { case ON_LEAVE: case STUDENT: case MISSING: + case ON_MATERNITY_LEAVE: case AWOL: assertTrue(personnelStatus.isAbsent()); break; @@ -355,11 +367,36 @@ void testGetImplementedStatuses() { void testParseFromString() { // Normal Parsing assertEquals(PersonnelStatus.RETIRED, PersonnelStatus.parseFromString("RETIRED")); - assertEquals(PersonnelStatus.PREGNANCY_COMPLICATIONS, - PersonnelStatus.parseFromString("PREGNANCY_COMPLICATIONS")); + assertEquals(PersonnelStatus.PREGNANCY_COMPLICATIONS, PersonnelStatus.parseFromString("PREGNANCY_COMPLICATIONS")); + + // Legacy Parsing + assertEquals(PersonnelStatus.ACTIVE, PersonnelStatus.parseFromString("0")); + assertEquals(PersonnelStatus.RETIRED, PersonnelStatus.parseFromString("1")); + assertEquals(PersonnelStatus.KIA, PersonnelStatus.parseFromString("2")); + 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")); + assertEquals(PersonnelStatus.SACKED, PersonnelStatus.parseFromString("22")); + assertEquals(PersonnelStatus.ON_MATERNITY_LEAVE, PersonnelStatus.parseFromString("23")); // Error Case - assertEquals(PersonnelStatus.ACTIVE, PersonnelStatus.parseFromString("23")); + assertEquals(PersonnelStatus.ACTIVE, PersonnelStatus.parseFromString("24")); assertEquals(PersonnelStatus.ACTIVE, PersonnelStatus.parseFromString("blah")); } // endregion File I/O diff --git a/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomDivorceMethodTest.java b/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomDivorceMethodTest.java index c3de04b461..f4fa738809 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomDivorceMethodTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomDivorceMethodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,11 +21,12 @@ import mekhq.MekHQ; import mekhq.campaign.CampaignOptions; import mekhq.campaign.personnel.divorce.DisabledRandomDivorce; -import mekhq.campaign.personnel.divorce.PercentageRandomDivorce; +import mekhq.campaign.personnel.divorce.RandomDivorce; import org.junit.jupiter.api.Test; import java.util.ResourceBundle; +import static megamek.client.ui.WrapLayout.wordWrap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -44,10 +45,10 @@ public class RandomDivorceMethodTest { //region Getters @Test public void testGetToolTipText() { - assertEquals(resources.getString("RandomDivorceMethod.NONE.toolTipText"), - RandomDivorceMethod.NONE.getToolTipText()); - assertEquals(resources.getString("RandomDivorceMethod.PERCENTAGE.toolTipText"), - RandomDivorceMethod.PERCENTAGE.getToolTipText()); + String expected = wordWrap(resources.getString("RandomDivorceMethod.NONE.toolTipText")).replaceAll("\\s", ""); + String actual = wordWrap(RandomDivorceMethod.NONE.getToolTipText().trim()).replaceAll("\\s", ""); + + assertEquals(expected, actual); } //endregion Getters @@ -64,12 +65,12 @@ public void testIsNone() { } @Test - public void testIsPercentage() { + public void testIsDiceRoll() { for (final RandomDivorceMethod randomDivorceMethod : methods) { - if (randomDivorceMethod == RandomDivorceMethod.PERCENTAGE) { - assertTrue(randomDivorceMethod.isPercentage()); + if (randomDivorceMethod == RandomDivorceMethod.DICE_ROLL) { + assertTrue(randomDivorceMethod.isDiceRoll()); } else { - assertFalse(randomDivorceMethod.isPercentage()); + assertFalse(randomDivorceMethod.isDiceRoll()); } } } @@ -84,18 +85,17 @@ public void testGetMethod() { when(mockOptions.isUseRandomSameSexDivorce()).thenReturn(false); when(mockOptions.isUseRandomClanPersonnelDivorce()).thenReturn(false); when(mockOptions.isUseRandomPrisonerDivorce()).thenReturn(false); - when(mockOptions.getPercentageRandomDivorceOppositeSexChance()).thenReturn(0.5); - when(mockOptions.getPercentageRandomDivorceSameSexChance()).thenReturn(0.5); + when(mockOptions.getRandomDivorceDiceSize()).thenReturn(5); assertInstanceOf(DisabledRandomDivorce.class, RandomDivorceMethod.NONE.getMethod(mockOptions)); - assertInstanceOf(PercentageRandomDivorce.class, RandomDivorceMethod.PERCENTAGE.getMethod(mockOptions)); + assertInstanceOf(RandomDivorce.class, RandomDivorceMethod.DICE_ROLL.getMethod(mockOptions)); } @Test public void testToStringOverride() { assertEquals(resources.getString("RandomDivorceMethod.NONE.text"), RandomDivorceMethod.NONE.toString()); - assertEquals(resources.getString("RandomDivorceMethod.PERCENTAGE.text"), - RandomDivorceMethod.PERCENTAGE.toString()); + assertEquals(resources.getString("RandomDivorceMethod.DICE_ROLL.text"), + RandomDivorceMethod.DICE_ROLL.toString()); } } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomMarriageMethodTest.java b/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomMarriageMethodTest.java index 76ea787648..425bcdf869 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomMarriageMethodTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomMarriageMethodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,11 +21,12 @@ import mekhq.MekHQ; import mekhq.campaign.CampaignOptions; import mekhq.campaign.personnel.marriage.DisabledRandomMarriage; -import mekhq.campaign.personnel.marriage.PercentageRandomMarriage; +import mekhq.campaign.personnel.marriage.RandomMarriage; import org.junit.jupiter.api.Test; import java.util.ResourceBundle; +import static megamek.client.ui.WrapLayout.wordWrap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -44,10 +45,10 @@ public class RandomMarriageMethodTest { //region Getters @Test public void testGetToolTipText() { - assertEquals(resources.getString("RandomMarriageMethod.NONE.toolTipText"), + assertEquals(wordWrap(resources.getString("RandomMarriageMethod.NONE.toolTipText")), RandomMarriageMethod.NONE.getToolTipText()); - assertEquals(resources.getString("RandomMarriageMethod.PERCENTAGE.toolTipText"), - RandomMarriageMethod.PERCENTAGE.getToolTipText()); + assertEquals(wordWrap(resources.getString("RandomMarriageMethod.DICE_ROLL.toolTipText")), + RandomMarriageMethod.DICE_ROLL.getToolTipText()); } //endregion Getters @@ -66,10 +67,10 @@ public void testIsNone() { @Test public void testIsPercentage() { for (final RandomMarriageMethod randomMarriageMethod : methods) { - if (randomMarriageMethod == RandomMarriageMethod.PERCENTAGE) { - assertTrue(randomMarriageMethod.isPercentage()); + if (randomMarriageMethod == RandomMarriageMethod.DICE_ROLL) { + assertTrue(randomMarriageMethod.isDiceRoll()); } else { - assertFalse(randomMarriageMethod.isPercentage()); + assertFalse(randomMarriageMethod.isDiceRoll()); } } } @@ -80,21 +81,19 @@ public void testGetMethod() { final CampaignOptions mockOptions = mock(CampaignOptions.class); when(mockOptions.isUseClanPersonnelMarriages()).thenReturn(false); when(mockOptions.isUsePrisonerMarriages()).thenReturn(false); - when(mockOptions.isUseRandomSameSexMarriages()).thenReturn(false); when(mockOptions.isUseRandomClanPersonnelMarriages()).thenReturn(false); when(mockOptions.isUseRandomPrisonerMarriages()).thenReturn(false); - when(mockOptions.getPercentageRandomMarriageOppositeSexChance()).thenReturn(0.5); - when(mockOptions.getPercentageRandomMarriageSameSexChance()).thenReturn(0.5); + when(mockOptions.getRandomMarriageDiceSize()).thenReturn(5); assertInstanceOf(DisabledRandomMarriage.class, RandomMarriageMethod.NONE.getMethod(mockOptions)); - assertInstanceOf(PercentageRandomMarriage.class, RandomMarriageMethod.PERCENTAGE.getMethod(mockOptions)); + assertInstanceOf(RandomMarriage.class, RandomMarriageMethod.DICE_ROLL.getMethod(mockOptions)); } @Test public void testToStringOverride() { - assertEquals(resources.getString("RandomMarriageMethod.NONE.text"), - RandomMarriageMethod.NONE.toString()); - assertEquals(resources.getString("RandomMarriageMethod.PERCENTAGE.text"), - RandomMarriageMethod.PERCENTAGE.toString()); + assertEquals(wordWrap(resources.getString("RandomMarriageMethod.NONE.text")).trim(), + RandomMarriageMethod.NONE.toString().trim()); + assertEquals(wordWrap(resources.getString("RandomMarriageMethod.DICE_ROLL.text")).trim(), + RandomMarriageMethod.DICE_ROLL.toString().trim()); } } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomProcreationMethodTest.java b/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomProcreationMethodTest.java index 7156a5ea25..a3f93be194 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomProcreationMethodTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/enums/RandomProcreationMethodTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -21,7 +21,7 @@ import mekhq.MekHQ; import mekhq.campaign.CampaignOptions; import mekhq.campaign.personnel.procreation.DisabledRandomProcreation; -import mekhq.campaign.personnel.procreation.PercentageRandomProcreation; +import mekhq.campaign.personnel.procreation.RandomProcreation; import org.junit.jupiter.api.Test; import java.util.ResourceBundle; @@ -44,10 +44,10 @@ public class RandomProcreationMethodTest { //region Getters @Test public void testGetToolTipText() { - assertEquals(resources.getString("RandomProcreationMethod.NONE.toolTipText"), - RandomProcreationMethod.NONE.getToolTipText()); - assertEquals(resources.getString("RandomProcreationMethod.PERCENTAGE.toolTipText"), - RandomProcreationMethod.PERCENTAGE.getToolTipText()); + assertEquals(resources.getString("RandomProcreationMethod.NONE.toolTipText").trim(), + RandomProcreationMethod.NONE.getToolTipText().trim()); + assertEquals(resources.getString("RandomProcreationMethod.DICE_ROLL.toolTipText").trim(), + RandomProcreationMethod.DICE_ROLL.getToolTipText().trim()); } //endregion Getters @@ -66,10 +66,10 @@ public void testIsNone() { @Test public void testIsPercentage() { for (final RandomProcreationMethod randomProcreationMethod : methods) { - if (randomProcreationMethod == RandomProcreationMethod.PERCENTAGE) { - assertTrue(randomProcreationMethod.isPercentage()); + if (randomProcreationMethod == RandomProcreationMethod.DICE_ROLL) { + assertTrue(randomProcreationMethod.isDiceRoll()); } else { - assertFalse(randomProcreationMethod.isPercentage()); + assertFalse(randomProcreationMethod.isDiceRoll()); } } } @@ -83,18 +83,18 @@ public void testGetMethod() { when(mockOptions.isUseRelationshiplessRandomProcreation()).thenReturn(false); when(mockOptions.isUseRandomClanPersonnelProcreation()).thenReturn(false); when(mockOptions.isUseRandomPrisonerProcreation()).thenReturn(false); - when(mockOptions.getPercentageRandomProcreationRelationshipChance()).thenReturn(0.5); - when(mockOptions.getPercentageRandomProcreationRelationshiplessChance()).thenReturn(0.5); + when(mockOptions.getRandomProcreationRelationshipDiceSize()).thenReturn(5); + when(mockOptions.getRandomProcreationRelationshiplessDiceSize()).thenReturn(5); assertInstanceOf(DisabledRandomProcreation.class, RandomProcreationMethod.NONE.getMethod(mockOptions)); - assertInstanceOf(PercentageRandomProcreation.class, RandomProcreationMethod.PERCENTAGE.getMethod(mockOptions)); + assertInstanceOf(RandomProcreation.class, RandomProcreationMethod.DICE_ROLL.getMethod(mockOptions)); } @Test public void testToStringOverride() { assertEquals(resources.getString("RandomProcreationMethod.NONE.text"), RandomProcreationMethod.NONE.toString()); - assertEquals(resources.getString("RandomProcreationMethod.PERCENTAGE.text"), - RandomProcreationMethod.PERCENTAGE.toString()); + assertEquals(resources.getString("RandomProcreationMethod.DICE_ROLL.text"), + RandomProcreationMethod.DICE_ROLL.toString()); } } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/marriage/AbstractMarriageTest.java b/MekHQ/unittests/mekhq/campaign/personnel/marriage/AbstractMarriageTest.java index 925d0e6371..b2bcd479ab 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/marriage/AbstractMarriageTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/marriage/AbstractMarriageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -63,7 +63,6 @@ public void beforeEach() { public void testGettersAndSetters() { when(mockCampaignOptions.isUseClanPersonnelMarriages()).thenReturn(false); when(mockCampaignOptions.isUsePrisonerMarriages()).thenReturn(false); - when(mockCampaignOptions.isUseRandomSameSexMarriages()).thenReturn(false); when(mockCampaignOptions.isUseRandomClanPersonnelMarriages()).thenReturn(false); when(mockCampaignOptions.isUseRandomPrisonerMarriages()).thenReturn(false); @@ -72,7 +71,6 @@ public void testGettersAndSetters() { assertEquals(RandomMarriageMethod.NONE, disabledMarriage.getMethod()); assertFalse(disabledMarriage.isUseClanPersonnelMarriages()); assertFalse(disabledMarriage.isUsePrisonerMarriages()); - assertFalse(disabledMarriage.isUseRandomSameSexMarriages()); assertFalse(disabledMarriage.isUseRandomClanPersonnelMarriages()); assertFalse(disabledMarriage.isUseRandomPrisonerMarriages()); } @@ -80,9 +78,7 @@ public void testGettersAndSetters() { @Test public void testCanMarry() { - doCallRealMethod().when(mockMarriage).canMarry(any(), any(), any(), anyBoolean()); - - when(mockCampaignOptions.getMinimumMarriageAge()).thenReturn(16); + doCallRealMethod().when(mockMarriage).canMarry(any(), any(), anyBoolean()); final Genealogy mockGenealogy = mock(Genealogy.class); @@ -91,77 +87,79 @@ public void testCanMarry() { // Have to be marriageable when(mockPerson.isMarriageable()).thenReturn(false); - assertNotNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false)); + assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); - // Can't be married already + // Can't be married when(mockPerson.isMarriageable()).thenReturn(true); when(mockGenealogy.hasSpouse()).thenReturn(true); - assertNotNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false)); + assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); // Must be active when(mockGenealogy.hasSpouse()).thenReturn(false); when(mockPerson.getStatus()).thenReturn(PersonnelStatus.KIA); - assertNotNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false)); + assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); // Can't be deployed when(mockPerson.getStatus()).thenReturn(PersonnelStatus.ACTIVE); when(mockPerson.isDeployed()).thenReturn(true); - assertNotNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false)); + assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); // Can't be younger than the minimum marriage age when(mockPerson.isDeployed()).thenReturn(false); - when(mockPerson.getAge(any())).thenReturn(15); - assertNotNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false)); + when(mockPerson.isChild(any())).thenReturn(true); + when(mockPerson.isChild(any())).thenReturn(true); + assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); // Can't be Clan Personnel with Clan Marriage Disabled - when(mockPerson.getAge(any())).thenReturn(16); + when(mockPerson.isChild(any())).thenReturn(false); when(mockPerson.isClanPersonnel()).thenReturn(true); - when(mockMarriage.isUseClanPersonnelMarriages()).thenReturn(false); - when(mockMarriage.isUsePrisonerMarriages()).thenReturn(true); - assertNotNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false)); + lenient().when(mockMarriage.isUseClanPersonnelMarriages()).thenReturn(false); + lenient().when(mockMarriage.isUsePrisonerMarriages()).thenReturn(true); + assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); // Can be Non-Clan Personnel with Clan Marriage Disabled when(mockPerson.isClanPersonnel()).thenReturn(false); - assertNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false)); + when(mockPerson.isChild(any())).thenReturn(false); + assertNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); // Can be a Non-Prisoner with Prisoner Marriage Disabled when(mockPerson.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); when(mockMarriage.isUsePrisonerMarriages()).thenReturn(false); - assertNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false)); + assertNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); // Can't be a Prisoner with Prisoner Marriage Disabled when(mockPerson.getPrisonerStatus()).thenReturn(PrisonerStatus.PRISONER); - assertNotNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false)); + assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); // Can be a Non-Random Clan Prisoner with Clan and Prisoner Marriage Enabled and Random Marriage Disabled when(mockPerson.isClanPersonnel()).thenReturn(true); when(mockMarriage.isUseClanPersonnelMarriages()).thenReturn(true); when(mockMarriage.isUsePrisonerMarriages()).thenReturn(true); - assertNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false)); + assertNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, false)); // Can't be Clan Personnel with Random Clan Marriage Disabled when(mockMarriage.isUseRandomClanPersonnelMarriages()).thenReturn(false); when(mockMarriage.isUseRandomPrisonerMarriages()).thenReturn(true); - assertNotNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true)); + assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, true)); // Can be Non-Clan Personnel with Random Clan Marriage Disabled when(mockPerson.isClanPersonnel()).thenReturn(false); - assertNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true)); + assertNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, true)); // Can be a Non-Prisoner with Random Prisoner Marriage Disabled when(mockPerson.getPrisonerStatus()).thenReturn(PrisonerStatus.FREE); when(mockMarriage.isUseRandomPrisonerMarriages()).thenReturn(false); - assertNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true)); + assertNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, true)); // Can't be a Prisoner with Random Prisoner Marriage Disabled when(mockPerson.getPrisonerStatus()).thenReturn(PrisonerStatus.PRISONER); - assertNotNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true)); + assertNotNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, true)); // Can be a Clan Prisoner with Random Clan and Random Prisoner Marriage Enabled lenient().when(mockPerson.isClanPersonnel()).thenReturn(true); when(mockMarriage.isUseRandomClanPersonnelMarriages()).thenReturn(true); when(mockMarriage.isUseRandomPrisonerMarriages()).thenReturn(true); - assertNull(mockMarriage.canMarry(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true)); + assertNull(mockMarriage.canMarry(LocalDate.ofYearDay(3025, 1), mockPerson, true)); } @Test @@ -179,11 +177,11 @@ public void testSafeSpouse() { assertFalse(mockMarriage.safeSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, mockPerson, false)); // Need to be able to marry - when(mockMarriage.canMarry(any(), any(), any(), anyBoolean())).thenReturn("Married"); + when(mockMarriage.canMarry(any(), any(), anyBoolean())).thenReturn("Married"); assertFalse(mockMarriage.safeSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, mockSpouse, false)); // Can't be closely related - when(mockMarriage.canMarry(any(), any(), any(), anyBoolean())).thenReturn(null); + when(mockMarriage.canMarry(any(), any(), anyBoolean())).thenReturn(null); when(mockGenealogy.checkMutualAncestors(any(), anyInt())).thenReturn(true); assertFalse(mockMarriage.safeSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, mockSpouse, false)); @@ -235,22 +233,25 @@ public void testSafeSpouse() { @Test public void testMarry() { - doCallRealMethod().when(mockMarriage).marry(any(), any(), any(), any(), any()); + doCallRealMethod().when(mockMarriage).marry(any(), any(), any(), any(), any(), anyBoolean()); when(mockCampaign.getRankSystem()).thenReturn(mock(RankSystem.class)); doNothing().when(mockCampaign).addReport(any()); final Person origin = new Person("Origin", "Origin", mockCampaign); + origin.setJoinedCampaign(LocalDate.ofYearDay(3025, 1)); + final Person spouse = new Person("Spouse", "Spouse", mockCampaign); + spouse.setJoinedCampaign(LocalDate.ofYearDay(3025, 1)); final MergingSurnameStyle mockMergingSurnameStyle = mock(MergingSurnameStyle.class); doNothing().when(mockMergingSurnameStyle).apply(any(), any(), any(), any()); - mockMarriage.marry(mockCampaign, LocalDate.ofYearDay(3025, 1), origin, null, mockMergingSurnameStyle); + mockMarriage.marry(mockCampaign, LocalDate.ofYearDay(3025, 1), origin, null, mockMergingSurnameStyle, false); assertNull(origin.getMaidenName()); assertFalse(origin.getGenealogy().hasSpouse()); - mockMarriage.marry(mockCampaign, LocalDate.ofYearDay(3025, 1), origin, spouse, mockMergingSurnameStyle); + mockMarriage.marry(mockCampaign, LocalDate.ofYearDay(3025, 1), origin, spouse, mockMergingSurnameStyle, false); assertEquals("Origin", origin.getMaidenName()); assertEquals("Spouse", spouse.getMaidenName()); assertEquals(origin, spouse.getGenealogy().getSpouse()); @@ -258,52 +259,31 @@ public void testMarry() { verify(mockMergingSurnameStyle, times(1)).apply(any(), any(), any(), any()); } - //region New Day + //region New Week @Test - public void testProcessNewDay() { - doCallRealMethod().when(mockMarriage).processNewDay(any(), any(), any()); - doNothing().when(mockMarriage).marryRandomSpouse(any(), any(), any(), anyBoolean()); + public void testProcessNewWeek() { + doCallRealMethod().when(mockMarriage).processNewWeek(any(), any(), any()); + doNothing().when(mockMarriage).marryRandomSpouse(any(), any(), any(), anyBoolean(), anyBoolean(), eq(true)); final Person mockPerson = mock(Person.class); - when(mockMarriage.canMarry(any(), any(), any(), anyBoolean())).thenReturn("Married"); - mockMarriage.processNewDay(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); - verify(mockMarriage, times(0)).randomOppositeSexMarriage(any()); - verify(mockMarriage, times(0)).randomSameSexMarriage(any()); - verify(mockMarriage, times(0)).marryRandomSpouse(any(), any(), any(), anyBoolean()); - - when(mockMarriage.canMarry(any(), any(), any(), anyBoolean())).thenReturn(null); - when(mockMarriage.randomOppositeSexMarriage(any())).thenReturn(true); - mockMarriage.processNewDay(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); - verify(mockMarriage, times(1)).randomOppositeSexMarriage(any()); - verify(mockMarriage, times(0)).randomSameSexMarriage(any()); - verify(mockMarriage, times(1)).marryRandomSpouse(any(), any(), any(), anyBoolean()); - - when(mockMarriage.randomOppositeSexMarriage(any())).thenReturn(false); - when(mockMarriage.isUseRandomSameSexMarriages()).thenReturn(false); - mockMarriage.processNewDay(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); - verify(mockMarriage, times(2)).randomOppositeSexMarriage(any()); - verify(mockMarriage, times(0)).randomSameSexMarriage(any()); - verify(mockMarriage, times(1)).marryRandomSpouse(any(), any(), any(), anyBoolean()); - - when(mockMarriage.isUseRandomSameSexMarriages()).thenReturn(true); - when(mockMarriage.randomSameSexMarriage(any())).thenReturn(false); - mockMarriage.processNewDay(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); - verify(mockMarriage, times(3)).randomOppositeSexMarriage(any()); - verify(mockMarriage, times(1)).randomSameSexMarriage(any()); - verify(mockMarriage, times(1)).marryRandomSpouse(any(), any(), any(), anyBoolean()); - - when(mockMarriage.randomSameSexMarriage(any())).thenReturn(true); - mockMarriage.processNewDay(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); - verify(mockMarriage, times(4)).randomOppositeSexMarriage(any()); - verify(mockMarriage, times(2)).randomSameSexMarriage(any()); - verify(mockMarriage, times(2)).marryRandomSpouse(any(), any(), any(), anyBoolean()); + when(mockMarriage.canMarry(any(), any(), anyBoolean())).thenReturn("Married"); + mockMarriage.processNewWeek(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); + verify(mockMarriage, times(0)).randomMarriage(); + verify(mockMarriage, times(0)).marryRandomSpouse(any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean()); + + when(mockMarriage.canMarry(any(), any(), anyBoolean())).thenReturn(null); + when(mockMarriage.randomMarriage()).thenReturn(true); + mockMarriage.processNewWeek(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); + verify(mockMarriage, times(1)).randomMarriage(); + verify(mockMarriage, times(1)).marryRandomSpouse(any(), any(), any(), anyBoolean(), anyBoolean(), anyBoolean()); } + //region Random Marriage @Test public void testMarryRandomSpouse() { - doCallRealMethod().when(mockMarriage).marryRandomSpouse(any(), any(), any(), anyBoolean()); + doCallRealMethod().when(mockMarriage).marryRandomSpouse(any(), any(), any(), anyBoolean(), eq(true), eq(true)); final Person mockMale = mock(Person.class); when(mockMale.getGender()).thenReturn(Gender.MALE); @@ -321,8 +301,7 @@ public void testMarryRandomSpouse() { // No Potential Spouses when(mockMarriage.isPotentialRandomSpouse(any(), any(), any(), any(), any())).thenReturn(false); - mockMarriage.marryRandomSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true); - verify(mockMarriage, times(0)).marry(any(), any(), any(), any(), any()); + mockMarriage.marryRandomSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true, true, true); // Replace AbstractMarriage::isPotentialRandomSpouse with this simple gender comparison doAnswer(invocation -> invocation.getArgument(3, Person.class).getGender() == invocation.getArgument(4)) @@ -333,16 +312,16 @@ public void testMarryRandomSpouse() { final Person spouse = invocation.getArgument(3); assertEquals(spouse, mockFemale); return null; - }).when(mockMarriage).marry(any(), any(), any(), any(), any()); - mockMarriage.marryRandomSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true); + }).when(mockMarriage).marry(any(), any(), any(), any(), any(), anyBoolean()); + mockMarriage.marryRandomSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true, true, true); // Opposite sex, Female: Expect marry to be called with mockMale as the spouse doAnswer(invocation -> { final Person spouse = invocation.getArgument(3); assertEquals(spouse, mockMale); return null; - }).when(mockMarriage).marry(any(), any(), any(), any(), any()); - mockMarriage.marryRandomSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false); + }).when(mockMarriage).marry(any(), any(), any(), any(), any(), anyBoolean()); + mockMarriage.marryRandomSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false, true, true); // Same-sex, Male: Expect marry to be called with mockMale as the spouse when(mockPerson.getGender()).thenReturn(Gender.MALE); @@ -350,16 +329,16 @@ public void testMarryRandomSpouse() { final Person spouse = invocation.getArgument(3); assertEquals(spouse, mockMale); return null; - }).when(mockMarriage).marry(any(), any(), any(), any(), any()); - mockMarriage.marryRandomSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true); + }).when(mockMarriage).marry(any(), any(), any(), any(), any(), anyBoolean()); + mockMarriage.marryRandomSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, true, true, true); // Opposite sex, Male: Expect marry to be called with mockFemale as the spouse doAnswer(invocation -> { final Person spouse = invocation.getArgument(3); assertEquals(spouse, mockFemale); return null; - }).when(mockMarriage).marry(any(), any(), any(), any(), any()); - mockMarriage.marryRandomSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false); + }).when(mockMarriage).marry(any(), any(), any(), any(), any(), anyBoolean()); + mockMarriage.marryRandomSpouse(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson, false, true, true); } @Test diff --git a/MekHQ/unittests/mekhq/campaign/personnel/marriage/DisabledRandomMarriageTest.java b/MekHQ/unittests/mekhq/campaign/personnel/marriage/DisabledRandomMarriageTest.java index e850434185..499f13de52 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/marriage/DisabledRandomMarriageTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/marriage/DisabledRandomMarriageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -41,18 +41,12 @@ public class DisabledRandomMarriageTest { public void beforeEach() { when(mockOptions.isUseClanPersonnelMarriages()).thenReturn(false); when(mockOptions.isUsePrisonerMarriages()).thenReturn(false); - when(mockOptions.isUseRandomSameSexMarriages()).thenReturn(false); when(mockOptions.isUseRandomClanPersonnelMarriages()).thenReturn(false); when(mockOptions.isUseRandomPrisonerMarriages()).thenReturn(false); } @Test - public void testRandomOppositeSexMarriage() { - assertFalse(new DisabledRandomMarriage(mockOptions).randomOppositeSexMarriage(mockPerson)); - } - - @Test - public void testRandomSameSexMarriage() { - assertFalse(new DisabledRandomMarriage(mockOptions).randomSameSexMarriage(mockPerson)); + public void testRandomMarriage() { + assertFalse(new DisabledRandomMarriage(mockOptions).randomMarriage()); } } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/marriage/PercentageRandomMarriageTest.java b/MekHQ/unittests/mekhq/campaign/personnel/marriage/PercentageRandomMarriageTest.java index 070edf7b8c..021ae51cba 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/marriage/PercentageRandomMarriageTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/marriage/PercentageRandomMarriageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -45,44 +45,22 @@ public class PercentageRandomMarriageTest { public void beforeEach() { when(mockOptions.isUseClanPersonnelMarriages()).thenReturn(false); when(mockOptions.isUsePrisonerMarriages()).thenReturn(false); - when(mockOptions.isUseRandomSameSexMarriages()).thenReturn(false); when(mockOptions.isUseRandomClanPersonnelMarriages()).thenReturn(false); when(mockOptions.isUseRandomPrisonerMarriages()).thenReturn(false); - when(mockOptions.getPercentageRandomMarriageOppositeSexChance()).thenReturn(0.5); - when(mockOptions.getPercentageRandomMarriageSameSexChance()).thenReturn(0.5); + when(mockOptions.getRandomMarriageDiceSize()).thenReturn(5); } @Test - public void testRandomOppositeSexMarriage() { - final PercentageRandomMarriage percentageRandomMarriage = new PercentageRandomMarriage(mockOptions); - // This ignores the person, so just using a mocked person - // Testing Minimum (0f), Below Value (0.49f), At Value (0.5f), and Maximum (1f) - try (MockedStatic compute = Mockito.mockStatic(Compute.class)) { - compute.when(Compute::randomFloat).thenReturn(0f); - assertTrue(percentageRandomMarriage.randomOppositeSexMarriage(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.49f); - assertTrue(percentageRandomMarriage.randomOppositeSexMarriage(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.5f); - assertFalse(percentageRandomMarriage.randomOppositeSexMarriage(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(1f); - assertFalse(percentageRandomMarriage.randomOppositeSexMarriage(mockPerson)); - } - } + public void testRandomMarriage() { + final RandomMarriage randomMarriage = new RandomMarriage(mockOptions); + + int diceSize = 5; - @Test - public void testRandomSameSexMarriage() { - final PercentageRandomMarriage percentageRandomMarriage = new PercentageRandomMarriage(mockOptions); - // This ignores the person, so just using a mocked person - // Testing Minimum (0f), Below Value (0.49f), At Value (0.5f), and Maximum (1f) try (MockedStatic compute = Mockito.mockStatic(Compute.class)) { - compute.when(Compute::randomFloat).thenReturn(0f); - assertTrue(percentageRandomMarriage.randomSameSexMarriage(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.49f); - assertTrue(percentageRandomMarriage.randomSameSexMarriage(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.5f); - assertFalse(percentageRandomMarriage.randomSameSexMarriage(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(1f); - assertFalse(percentageRandomMarriage.randomSameSexMarriage(mockPerson)); + compute.when(() -> Compute.randomInt(diceSize)).thenReturn(0); + assertTrue(randomMarriage.randomMarriage()); + compute.when(() -> Compute.randomInt(diceSize)).thenReturn(1); + assertFalse(randomMarriage.randomMarriage()); } } } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/procreation/AbstractProcreationTest.java b/MekHQ/unittests/mekhq/campaign/personnel/procreation/AbstractProcreationTest.java index 4512439ee4..7cdafedfae 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/procreation/AbstractProcreationTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/procreation/AbstractProcreationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -299,20 +299,20 @@ public void testCanProcreate() { @Test public void testAddPregnancy() { - doCallRealMethod().when(mockProcreation).addPregnancy(any(), any(), any()); - doCallRealMethod().when(mockProcreation).addPregnancy(any(), any(), any(), anyInt()); + doCallRealMethod().when(mockProcreation).addPregnancy(any(), any(), any(), eq(false)); + doCallRealMethod().when(mockProcreation).addPregnancy(any(), any(), any(), anyInt(), eq(false)); final Person mother = new Person(mockCampaign); final Person father = new Person(mockCampaign); when(mockProcreation.determineNumberOfBabies(anyInt())).thenReturn(0); - mockProcreation.addPregnancy(mockCampaign, LocalDate.ofYearDay(3025, 1), mother); + mockProcreation.addPregnancy(mockCampaign, LocalDate.ofYearDay(3025, 1), mother, false); assertNull(mother.getExpectedDueDate()); assertNull(mother.getDueDate()); assertTrue(mother.getExtraData().isEmpty()); when(mockCampaignOptions.isLogProcreation()).thenReturn(false); - mockProcreation.addPregnancy(mockCampaign, LocalDate.ofYearDay(3025, 1), mother, 1); + mockProcreation.addPregnancy(mockCampaign, LocalDate.ofYearDay(3025, 1), mother, 1, false); assertEquals(LocalDate.ofYearDay(3025, 281), mother.getExpectedDueDate()); assertNotNull(mother.getDueDate()); assertFalse(mother.getExtraData().isEmpty()); @@ -321,7 +321,7 @@ public void testAddPregnancy() { assertEquals(1, mother.getExtraData().get(AbstractProcreation.PREGNANCY_CHILDREN_DATA)); when(mockCampaignOptions.isLogProcreation()).thenReturn(true); - mockProcreation.addPregnancy(mockCampaign, LocalDate.ofYearDay(3025, 1), mother, 2); + mockProcreation.addPregnancy(mockCampaign, LocalDate.ofYearDay(3025, 1), mother, 2, false); assertEquals(LocalDate.ofYearDay(3025, 281), mother.getExpectedDueDate()); assertNotNull(mother.getDueDate()); assertFalse(mother.getExtraData().isEmpty()); @@ -330,7 +330,7 @@ public void testAddPregnancy() { assertEquals(2, mother.getExtraData().get(AbstractProcreation.PREGNANCY_CHILDREN_DATA)); mother.getGenealogy().setSpouse(father); - mockProcreation.addPregnancy(mockCampaign, LocalDate.ofYearDay(3025, 1), mother, 10); + mockProcreation.addPregnancy(mockCampaign, LocalDate.ofYearDay(3025, 1), mother, 10, false); assertEquals(LocalDate.ofYearDay(3025, 281), mother.getExpectedDueDate()); assertNotNull(mother.getDueDate()); assertFalse(mother.getExtraData().isEmpty()); @@ -447,41 +447,28 @@ public void testProcessPregnancyComplications() { //region New Day @Test - public void testProcessNewDay() { - doCallRealMethod().when(mockProcreation).processNewDay(any(), any(), any()); + public void testProcessNewWeek() { + doCallRealMethod().when(mockProcreation).processNewWeek(any(), any(), any()); doNothing().when(mockProcreation).birth(any(), any(), any()); - doNothing().when(mockProcreation).addPregnancy(any(), any(), any()); final Person mockPerson = mock(Person.class); when(mockPerson.getGender()).thenReturn(Gender.MALE); - mockProcreation.processNewDay(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); + mockProcreation.processNewWeek(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); verify(mockPerson, never()).isPregnant(); verify(mockProcreation, never()).randomlyProcreates(any(), any()); when(mockPerson.getGender()).thenReturn(Gender.FEMALE); when(mockPerson.isPregnant()).thenReturn(true); when(mockPerson.getDueDate()).thenReturn(LocalDate.ofYearDay(3025, 2)); - mockProcreation.processNewDay(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); + mockProcreation.processNewWeek(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); verify(mockProcreation, never()).birth(any(), any(), any()); verify(mockProcreation, never()).randomlyProcreates(any(), any()); when(mockPerson.getDueDate()).thenReturn(LocalDate.ofYearDay(3025, 1)); - mockProcreation.processNewDay(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); + mockProcreation.processNewWeek(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); verify(mockProcreation, times(1)).birth(any(), any(), any()); verify(mockProcreation, never()).randomlyProcreates(any(), any()); - - when(mockPerson.isPregnant()).thenReturn(false); - - when(mockProcreation.randomlyProcreates(any(), any())).thenReturn(false); - mockProcreation.processNewDay(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); - verify(mockProcreation, times(1)).birth(any(), any(), any()); - verify(mockProcreation, times(1)).randomlyProcreates(any(), any()); - - when(mockProcreation.randomlyProcreates(any(), any())).thenReturn(true); - mockProcreation.processNewDay(mockCampaign, LocalDate.ofYearDay(3025, 1), mockPerson); - verify(mockProcreation, times(2)).randomlyProcreates(any(), any()); - verify(mockProcreation, times(1)).addPregnancy(any(), any(), any()); } //region Random Procreation @@ -494,18 +481,19 @@ public void testRandomlyProcreates() { when(mockProcreation.canProcreate(any(), any(), anyBoolean())).thenReturn("Pregnant"); assertFalse(mockProcreation.randomlyProcreates(LocalDate.ofYearDay(3025, 1), person)); + reset(mockProcreation); + doCallRealMethod().when(mockProcreation).randomlyProcreates(any(), any()); + when(mockProcreation.canProcreate(any(), any(), anyBoolean())).thenReturn(null); when(mockProcreation.isUseRelationshiplessProcreation()).thenReturn(false); assertFalse(mockProcreation.randomlyProcreates(LocalDate.ofYearDay(3025, 1), person)); - when(mockProcreation.isUseRelationshiplessProcreation()).thenReturn(true); - when(mockProcreation.relationshiplessProcreation(any())).thenReturn(true); - assertTrue(mockProcreation.randomlyProcreates(LocalDate.ofYearDay(3025, 1), person)); + reset(mockProcreation); + doCallRealMethod().when(mockProcreation).randomlyProcreates(any(), any()); person.getGenealogy().setSpouse(mock(Person.class)); - when(mockProcreation.relationshipProcreation(any())).thenReturn(true); + when(mockProcreation.canProcreate(any(), any(), anyBoolean())).thenReturn(null); + when(mockProcreation.procreation(any())).thenReturn(true); assertTrue(mockProcreation.randomlyProcreates(LocalDate.ofYearDay(3025, 1), person)); } - //endregion Random Procreation - //endregion New Day } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/procreation/DisabledRandomProcreationTest.java b/MekHQ/unittests/mekhq/campaign/personnel/procreation/DisabledRandomProcreationTest.java index 41470c0a69..72ce804322 100644 --- a/MekHQ/unittests/mekhq/campaign/personnel/procreation/DisabledRandomProcreationTest.java +++ b/MekHQ/unittests/mekhq/campaign/personnel/procreation/DisabledRandomProcreationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2022-2024 - The MegaMek Team. All Rights Reserved. * * This file is part of MekHQ. * @@ -55,11 +55,11 @@ public void testRandomlyProcreates() { @Test public void testRandomlyDies() { - assertFalse(new DisabledRandomProcreation(mockOptions).relationshipProcreation(mockPerson)); + assertFalse(new DisabledRandomProcreation(mockOptions).procreation(mockPerson)); } @Test public void testRelationshiplessProcreation() { - assertFalse(new DisabledRandomProcreation(mockOptions).relationshiplessProcreation(mockPerson)); + assertFalse(new DisabledRandomProcreation(mockOptions).procreation(mockPerson)); } } diff --git a/MekHQ/unittests/mekhq/campaign/personnel/procreation/PercentageRandomProcreationTest.java b/MekHQ/unittests/mekhq/campaign/personnel/procreation/PercentageRandomProcreationTest.java deleted file mode 100644 index 9a322d582f..0000000000 --- a/MekHQ/unittests/mekhq/campaign/personnel/procreation/PercentageRandomProcreationTest.java +++ /dev/null @@ -1,88 +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.procreation; - -import megamek.common.Compute; -import mekhq.campaign.CampaignOptions; -import mekhq.campaign.personnel.Person; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.when; - -@ExtendWith(value = MockitoExtension.class) -public class PercentageRandomProcreationTest { - @Mock - private CampaignOptions mockOptions; - - @Mock - private Person mockPerson; - - @BeforeEach - public void beforeEach() { - when(mockOptions.isUseClanPersonnelProcreation()).thenReturn(false); - when(mockOptions.isUsePrisonerProcreation()).thenReturn(false); - when(mockOptions.isUseRelationshiplessRandomProcreation()).thenReturn(false); - when(mockOptions.isUseRandomClanPersonnelProcreation()).thenReturn(false); - when(mockOptions.isUseRandomPrisonerProcreation()).thenReturn(false); - when(mockOptions.getPercentageRandomProcreationRelationshipChance()).thenReturn(0.5); - when(mockOptions.getPercentageRandomProcreationRelationshiplessChance()).thenReturn(0.5); - } - - @Test - public void testRelationshipProcreation() { - final PercentageRandomProcreation percentageRandomProcreation = new PercentageRandomProcreation(mockOptions); - // This ignores the person, so just using a mocked person - // Testing Minimum (0f), Below Value (0.49f), At Value (0.5f), and Maximum (1f) - try (MockedStatic compute = Mockito.mockStatic(Compute.class)) { - compute.when(Compute::randomFloat).thenReturn(0f); - assertTrue(percentageRandomProcreation.relationshipProcreation(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.49f); - assertTrue(percentageRandomProcreation.relationshipProcreation(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.5f); - assertFalse(percentageRandomProcreation.relationshipProcreation(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(1f); - assertFalse(percentageRandomProcreation.relationshipProcreation(mockPerson)); - } - } - - @Test - public void testRelationshiplessProcreation() { - final PercentageRandomProcreation percentageRandomProcreation = new PercentageRandomProcreation(mockOptions); - // This ignores the person, so just using a mocked person - // Testing Minimum (0f), Below Value (0.49f), At Value (0.5f), and Maximum (1f) - try (MockedStatic compute = Mockito.mockStatic(Compute.class)) { - compute.when(Compute::randomFloat).thenReturn(0f); - assertTrue(percentageRandomProcreation.relationshiplessProcreation(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.49f); - assertTrue(percentageRandomProcreation.relationshiplessProcreation(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(0.5f); - assertFalse(percentageRandomProcreation.relationshiplessProcreation(mockPerson)); - compute.when(Compute::randomFloat).thenReturn(1f); - assertFalse(percentageRandomProcreation.relationshiplessProcreation(mockPerson)); - } - } -} diff --git a/MekHQ/unittests/mekhq/campaign/personnel/procreation/RandomProcreationTest.java b/MekHQ/unittests/mekhq/campaign/personnel/procreation/RandomProcreationTest.java new file mode 100644 index 0000000000..b54eb39a98 --- /dev/null +++ b/MekHQ/unittests/mekhq/campaign/personnel/procreation/RandomProcreationTest.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022-2024 - 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.procreation; + +import megamek.common.Compute; +import mekhq.campaign.CampaignOptions; +import mekhq.campaign.personnel.Person; +import mekhq.campaign.personnel.familyTree.Genealogy; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(value = MockitoExtension.class) +public class RandomProcreationTest { + @Mock + private CampaignOptions mockOptions; + + @Mock + private Person mockPerson; + + @BeforeEach + public void beforeEach() { + when(mockOptions.isUseClanPersonnelProcreation()).thenReturn(false); + when(mockOptions.isUsePrisonerProcreation()).thenReturn(false); + when(mockOptions.isUseRelationshiplessRandomProcreation()).thenReturn(false); + when(mockOptions.isUseRandomClanPersonnelProcreation()).thenReturn(false); + when(mockOptions.isUseRandomPrisonerProcreation()).thenReturn(false); + when(mockOptions.getRandomProcreationRelationshipDiceSize()).thenReturn(5); + when(mockOptions.getRandomProcreationRelationshiplessDiceSize()).thenReturn(5); + } + + @Test + public void testProcreation() { + final RandomProcreation randomProcreation = new RandomProcreation(mockOptions); + Genealogy mockGenealogy = mock(Genealogy.class); + + when(mockGenealogy.hasSpouse()).thenReturn(true); + when(mockPerson.getGenealogy()).thenReturn(mockGenealogy); + + int diceSize = 5; + + try (MockedStatic compute = Mockito.mockStatic(Compute.class)) { + compute.when(() -> Compute.randomInt(diceSize)).thenReturn(0); + assertTrue(randomProcreation.procreation(mockPerson)); + compute.when(() -> Compute.randomInt(diceSize)).thenReturn(1); + assertFalse(randomProcreation.procreation(mockPerson)); + } + } +}